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/* Device controlling the backlight and associated keys */
 116struct fujitsu_bl {
 117        struct input_dev *input;
 118        char phys[32];
 119        struct backlight_device *bl_device;
 120        unsigned int max_brightness;
 121        unsigned int brightness_level;
 122};
 123
 124static struct fujitsu_bl *fujitsu_bl;
 125static int use_alt_lcd_levels = -1;
 126static bool disable_brightness_adjust;
 127
 128/* Device used to access hotkeys and other features on the laptop */
 129struct fujitsu_laptop {
 130        struct input_dev *input;
 131        char phys[32];
 132        struct platform_device *pf_device;
 133        struct kfifo fifo;
 134        spinlock_t fifo_lock;
 135        int flags_supported;
 136        int flags_state;
 137};
 138
 139static struct acpi_device *fext;
 140
 141/* Fujitsu ACPI interface function */
 142
 143static int call_fext_func(struct acpi_device *device,
 144                          int func, int op, int feature, int state)
 145{
 146        union acpi_object params[4] = {
 147                { .integer.type = ACPI_TYPE_INTEGER, .integer.value = func },
 148                { .integer.type = ACPI_TYPE_INTEGER, .integer.value = op },
 149                { .integer.type = ACPI_TYPE_INTEGER, .integer.value = feature },
 150                { .integer.type = ACPI_TYPE_INTEGER, .integer.value = state }
 151        };
 152        struct acpi_object_list arg_list = { 4, params };
 153        unsigned long long value;
 154        acpi_status status;
 155
 156        status = acpi_evaluate_integer(device->handle, "FUNC", &arg_list,
 157                                       &value);
 158        if (ACPI_FAILURE(status)) {
 159                acpi_handle_err(device->handle, "Failed to evaluate FUNC\n");
 160                return -ENODEV;
 161        }
 162
 163        acpi_handle_debug(device->handle,
 164                          "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n",
 165                          func, op, feature, state, (int)value);
 166        return value;
 167}
 168
 169/* Hardware access for LCD brightness control */
 170
 171static int set_lcd_level(struct acpi_device *device, int level)
 172{
 173        struct fujitsu_bl *priv = acpi_driver_data(device);
 174        acpi_status status;
 175        char *method;
 176
 177        switch (use_alt_lcd_levels) {
 178        case -1:
 179                if (acpi_has_method(device->handle, "SBL2"))
 180                        method = "SBL2";
 181                else
 182                        method = "SBLL";
 183                break;
 184        case 1:
 185                method = "SBL2";
 186                break;
 187        default:
 188                method = "SBLL";
 189                break;
 190        }
 191
 192        acpi_handle_debug(device->handle, "set lcd level via %s [%d]\n", method,
 193                          level);
 194
 195        if (level < 0 || level >= priv->max_brightness)
 196                return -EINVAL;
 197
 198        status = acpi_execute_simple_method(device->handle, method, level);
 199        if (ACPI_FAILURE(status)) {
 200                acpi_handle_err(device->handle, "Failed to evaluate %s\n",
 201                                method);
 202                return -ENODEV;
 203        }
 204
 205        priv->brightness_level = level;
 206
 207        return 0;
 208}
 209
 210static int get_lcd_level(struct acpi_device *device)
 211{
 212        struct fujitsu_bl *priv = acpi_driver_data(device);
 213        unsigned long long state = 0;
 214        acpi_status status = AE_OK;
 215
 216        acpi_handle_debug(device->handle, "get lcd level via GBLL\n");
 217
 218        status = acpi_evaluate_integer(device->handle, "GBLL", NULL, &state);
 219        if (ACPI_FAILURE(status))
 220                return 0;
 221
 222        priv->brightness_level = state & 0x0fffffff;
 223
 224        return priv->brightness_level;
 225}
 226
 227static int get_max_brightness(struct acpi_device *device)
 228{
 229        struct fujitsu_bl *priv = acpi_driver_data(device);
 230        unsigned long long state = 0;
 231        acpi_status status = AE_OK;
 232
 233        acpi_handle_debug(device->handle, "get max lcd level via RBLL\n");
 234
 235        status = acpi_evaluate_integer(device->handle, "RBLL", NULL, &state);
 236        if (ACPI_FAILURE(status))
 237                return -1;
 238
 239        priv->max_brightness = state;
 240
 241        return priv->max_brightness;
 242}
 243
 244/* Backlight device stuff */
 245
 246static int bl_get_brightness(struct backlight_device *b)
 247{
 248        struct acpi_device *device = bl_get_data(b);
 249
 250        return b->props.power == FB_BLANK_POWERDOWN ? 0 : get_lcd_level(device);
 251}
 252
 253static int bl_update_status(struct backlight_device *b)
 254{
 255        struct acpi_device *device = bl_get_data(b);
 256
 257        if (b->props.power == FB_BLANK_POWERDOWN)
 258                call_fext_func(fext, FUNC_BACKLIGHT, 0x1, 0x4, 0x3);
 259        else
 260                call_fext_func(fext, FUNC_BACKLIGHT, 0x1, 0x4, 0x0);
 261
 262        return set_lcd_level(device, b->props.brightness);
 263}
 264
 265static const struct backlight_ops fujitsu_bl_ops = {
 266        .get_brightness = bl_get_brightness,
 267        .update_status = bl_update_status,
 268};
 269
 270static ssize_t lid_show(struct device *dev, struct device_attribute *attr,
 271                        char *buf)
 272{
 273        struct fujitsu_laptop *priv = dev_get_drvdata(dev);
 274
 275        if (!(priv->flags_supported & FLAG_LID))
 276                return sprintf(buf, "unknown\n");
 277        if (priv->flags_state & FLAG_LID)
 278                return sprintf(buf, "open\n");
 279        else
 280                return sprintf(buf, "closed\n");
 281}
 282
 283static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
 284                         char *buf)
 285{
 286        struct fujitsu_laptop *priv = dev_get_drvdata(dev);
 287
 288        if (!(priv->flags_supported & FLAG_DOCK))
 289                return sprintf(buf, "unknown\n");
 290        if (priv->flags_state & FLAG_DOCK)
 291                return sprintf(buf, "docked\n");
 292        else
 293                return sprintf(buf, "undocked\n");
 294}
 295
 296static ssize_t radios_show(struct device *dev, struct device_attribute *attr,
 297                           char *buf)
 298{
 299        struct fujitsu_laptop *priv = dev_get_drvdata(dev);
 300
 301        if (!(priv->flags_supported & FLAG_RFKILL))
 302                return sprintf(buf, "unknown\n");
 303        if (priv->flags_state & FLAG_RFKILL)
 304                return sprintf(buf, "on\n");
 305        else
 306                return sprintf(buf, "killed\n");
 307}
 308
 309static DEVICE_ATTR_RO(lid);
 310static DEVICE_ATTR_RO(dock);
 311static DEVICE_ATTR_RO(radios);
 312
 313static struct attribute *fujitsu_pf_attributes[] = {
 314        &dev_attr_lid.attr,
 315        &dev_attr_dock.attr,
 316        &dev_attr_radios.attr,
 317        NULL
 318};
 319
 320static const struct attribute_group fujitsu_pf_attribute_group = {
 321        .attrs = fujitsu_pf_attributes
 322};
 323
 324static struct platform_driver fujitsu_pf_driver = {
 325        .driver = {
 326                   .name = "fujitsu-laptop",
 327                   }
 328};
 329
 330/* ACPI device for LCD brightness control */
 331
 332static const struct key_entry keymap_backlight[] = {
 333        { KE_KEY, true, { KEY_BRIGHTNESSUP } },
 334        { KE_KEY, false, { KEY_BRIGHTNESSDOWN } },
 335        { KE_END, 0 }
 336};
 337
 338static int acpi_fujitsu_bl_input_setup(struct acpi_device *device)
 339{
 340        struct fujitsu_bl *priv = acpi_driver_data(device);
 341        int ret;
 342
 343        priv->input = devm_input_allocate_device(&device->dev);
 344        if (!priv->input)
 345                return -ENOMEM;
 346
 347        snprintf(priv->phys, sizeof(priv->phys), "%s/video/input0",
 348                 acpi_device_hid(device));
 349
 350        priv->input->name = acpi_device_name(device);
 351        priv->input->phys = priv->phys;
 352        priv->input->id.bustype = BUS_HOST;
 353        priv->input->id.product = 0x06;
 354
 355        ret = sparse_keymap_setup(priv->input, keymap_backlight, NULL);
 356        if (ret)
 357                return ret;
 358
 359        return input_register_device(priv->input);
 360}
 361
 362static int fujitsu_backlight_register(struct acpi_device *device)
 363{
 364        struct fujitsu_bl *priv = acpi_driver_data(device);
 365        const struct backlight_properties props = {
 366                .brightness = priv->brightness_level,
 367                .max_brightness = priv->max_brightness - 1,
 368                .type = BACKLIGHT_PLATFORM
 369        };
 370        struct backlight_device *bd;
 371
 372        bd = devm_backlight_device_register(&device->dev, "fujitsu-laptop",
 373                                            &device->dev, device,
 374                                            &fujitsu_bl_ops, &props);
 375        if (IS_ERR(bd))
 376                return PTR_ERR(bd);
 377
 378        priv->bl_device = bd;
 379
 380        return 0;
 381}
 382
 383static int acpi_fujitsu_bl_add(struct acpi_device *device)
 384{
 385        struct fujitsu_bl *priv;
 386        int error;
 387
 388        if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
 389                return -ENODEV;
 390
 391        priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
 392        if (!priv)
 393                return -ENOMEM;
 394
 395        fujitsu_bl = priv;
 396        strcpy(acpi_device_name(device), ACPI_FUJITSU_BL_DEVICE_NAME);
 397        strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS);
 398        device->driver_data = priv;
 399
 400        error = acpi_fujitsu_bl_input_setup(device);
 401        if (error)
 402                return error;
 403
 404        pr_info("ACPI: %s [%s]\n",
 405                acpi_device_name(device), acpi_device_bid(device));
 406
 407        if (get_max_brightness(device) <= 0)
 408                priv->max_brightness = FUJITSU_LCD_N_LEVELS;
 409        get_lcd_level(device);
 410
 411        error = fujitsu_backlight_register(device);
 412        if (error)
 413                return error;
 414
 415        return 0;
 416}
 417
 418/* Brightness notify */
 419
 420static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event)
 421{
 422        struct fujitsu_bl *priv = acpi_driver_data(device);
 423        int oldb, newb;
 424
 425        if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
 426                acpi_handle_info(device->handle, "unsupported event [0x%x]\n",
 427                                 event);
 428                sparse_keymap_report_event(priv->input, -1, 1, true);
 429                return;
 430        }
 431
 432        oldb = priv->brightness_level;
 433        get_lcd_level(device);
 434        newb = priv->brightness_level;
 435
 436        acpi_handle_debug(device->handle,
 437                          "brightness button event [%i -> %i]\n", oldb, newb);
 438
 439        if (oldb == newb)
 440                return;
 441
 442        if (!disable_brightness_adjust)
 443                set_lcd_level(device, newb);
 444
 445        sparse_keymap_report_event(priv->input, oldb < newb, 1, true);
 446}
 447
 448/* ACPI device for hotkey handling */
 449
 450static const struct key_entry keymap_default[] = {
 451        { KE_KEY, KEY1_CODE, { KEY_PROG1 } },
 452        { KE_KEY, KEY2_CODE, { KEY_PROG2 } },
 453        { KE_KEY, KEY3_CODE, { KEY_PROG3 } },
 454        { KE_KEY, KEY4_CODE, { KEY_PROG4 } },
 455        { KE_KEY, KEY5_CODE, { KEY_RFKILL } },
 456        { KE_KEY, BIT(26),   { KEY_TOUCHPAD_TOGGLE } },
 457        { KE_END, 0 }
 458};
 459
 460static const struct key_entry keymap_s64x0[] = {
 461        { KE_KEY, KEY1_CODE, { KEY_SCREENLOCK } },      /* "Lock" */
 462        { KE_KEY, KEY2_CODE, { KEY_HELP } },            /* "Mobility Center */
 463        { KE_KEY, KEY3_CODE, { KEY_PROG3 } },
 464        { KE_KEY, KEY4_CODE, { KEY_PROG4 } },
 465        { KE_END, 0 }
 466};
 467
 468static const struct key_entry keymap_p8010[] = {
 469        { KE_KEY, KEY1_CODE, { KEY_HELP } },            /* "Support" */
 470        { KE_KEY, KEY2_CODE, { KEY_PROG2 } },
 471        { KE_KEY, KEY3_CODE, { KEY_SWITCHVIDEOMODE } }, /* "Presentation" */
 472        { KE_KEY, KEY4_CODE, { KEY_WWW } },             /* "WWW" */
 473        { KE_END, 0 }
 474};
 475
 476static const struct key_entry *keymap = keymap_default;
 477
 478static int fujitsu_laptop_dmi_keymap_override(const struct dmi_system_id *id)
 479{
 480        pr_info("Identified laptop model '%s'\n", id->ident);
 481        keymap = id->driver_data;
 482        return 1;
 483}
 484
 485static const struct dmi_system_id fujitsu_laptop_dmi_table[] = {
 486        {
 487                .callback = fujitsu_laptop_dmi_keymap_override,
 488                .ident = "Fujitsu Siemens S6410",
 489                .matches = {
 490                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
 491                        DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"),
 492                },
 493                .driver_data = (void *)keymap_s64x0
 494        },
 495        {
 496                .callback = fujitsu_laptop_dmi_keymap_override,
 497                .ident = "Fujitsu Siemens S6420",
 498                .matches = {
 499                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
 500                        DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"),
 501                },
 502                .driver_data = (void *)keymap_s64x0
 503        },
 504        {
 505                .callback = fujitsu_laptop_dmi_keymap_override,
 506                .ident = "Fujitsu LifeBook P8010",
 507                .matches = {
 508                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
 509                        DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P8010"),
 510                },
 511                .driver_data = (void *)keymap_p8010
 512        },
 513        {}
 514};
 515
 516static int acpi_fujitsu_laptop_input_setup(struct acpi_device *device)
 517{
 518        struct fujitsu_laptop *priv = acpi_driver_data(device);
 519        int ret;
 520
 521        priv->input = devm_input_allocate_device(&device->dev);
 522        if (!priv->input)
 523                return -ENOMEM;
 524
 525        snprintf(priv->phys, sizeof(priv->phys), "%s/input0",
 526                 acpi_device_hid(device));
 527
 528        priv->input->name = acpi_device_name(device);
 529        priv->input->phys = priv->phys;
 530        priv->input->id.bustype = BUS_HOST;
 531
 532        dmi_check_system(fujitsu_laptop_dmi_table);
 533        ret = sparse_keymap_setup(priv->input, keymap, NULL);
 534        if (ret)
 535                return ret;
 536
 537        return input_register_device(priv->input);
 538}
 539
 540static int fujitsu_laptop_platform_add(struct acpi_device *device)
 541{
 542        struct fujitsu_laptop *priv = acpi_driver_data(device);
 543        int ret;
 544
 545        priv->pf_device = platform_device_alloc("fujitsu-laptop", -1);
 546        if (!priv->pf_device)
 547                return -ENOMEM;
 548
 549        platform_set_drvdata(priv->pf_device, priv);
 550
 551        ret = platform_device_add(priv->pf_device);
 552        if (ret)
 553                goto err_put_platform_device;
 554
 555        ret = sysfs_create_group(&priv->pf_device->dev.kobj,
 556                                 &fujitsu_pf_attribute_group);
 557        if (ret)
 558                goto err_del_platform_device;
 559
 560        return 0;
 561
 562err_del_platform_device:
 563        platform_device_del(priv->pf_device);
 564err_put_platform_device:
 565        platform_device_put(priv->pf_device);
 566
 567        return ret;
 568}
 569
 570static void fujitsu_laptop_platform_remove(struct acpi_device *device)
 571{
 572        struct fujitsu_laptop *priv = acpi_driver_data(device);
 573
 574        sysfs_remove_group(&priv->pf_device->dev.kobj,
 575                           &fujitsu_pf_attribute_group);
 576        platform_device_unregister(priv->pf_device);
 577}
 578
 579static int logolamp_set(struct led_classdev *cdev,
 580                        enum led_brightness brightness)
 581{
 582        struct acpi_device *device = to_acpi_device(cdev->dev->parent);
 583        int poweron = FUNC_LED_ON, always = FUNC_LED_ON;
 584        int ret;
 585
 586        if (brightness < LED_HALF)
 587                poweron = FUNC_LED_OFF;
 588
 589        if (brightness < LED_FULL)
 590                always = FUNC_LED_OFF;
 591
 592        ret = call_fext_func(device, FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron);
 593        if (ret < 0)
 594                return ret;
 595
 596        return call_fext_func(device, FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always);
 597}
 598
 599static enum led_brightness logolamp_get(struct led_classdev *cdev)
 600{
 601        struct acpi_device *device = to_acpi_device(cdev->dev->parent);
 602        int ret;
 603
 604        ret = call_fext_func(device, FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0);
 605        if (ret == FUNC_LED_ON)
 606                return LED_FULL;
 607
 608        ret = call_fext_func(device, FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0);
 609        if (ret == FUNC_LED_ON)
 610                return LED_HALF;
 611
 612        return LED_OFF;
 613}
 614
 615static int kblamps_set(struct led_classdev *cdev,
 616                       enum led_brightness brightness)
 617{
 618        struct acpi_device *device = to_acpi_device(cdev->dev->parent);
 619
 620        if (brightness >= LED_FULL)
 621                return call_fext_func(device, FUNC_LEDS, 0x1, KEYBOARD_LAMPS,
 622                                      FUNC_LED_ON);
 623        else
 624                return call_fext_func(device, FUNC_LEDS, 0x1, KEYBOARD_LAMPS,
 625                                      FUNC_LED_OFF);
 626}
 627
 628static enum led_brightness kblamps_get(struct led_classdev *cdev)
 629{
 630        struct acpi_device *device = to_acpi_device(cdev->dev->parent);
 631        enum led_brightness brightness = LED_OFF;
 632
 633        if (call_fext_func(device,
 634                           FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON)
 635                brightness = LED_FULL;
 636
 637        return brightness;
 638}
 639
 640static int radio_led_set(struct led_classdev *cdev,
 641                         enum led_brightness brightness)
 642{
 643        struct acpi_device *device = to_acpi_device(cdev->dev->parent);
 644
 645        if (brightness >= LED_FULL)
 646                return call_fext_func(device, FUNC_FLAGS, 0x5, RADIO_LED_ON,
 647                                      RADIO_LED_ON);
 648        else
 649                return call_fext_func(device, FUNC_FLAGS, 0x5, RADIO_LED_ON,
 650                                      0x0);
 651}
 652
 653static enum led_brightness radio_led_get(struct led_classdev *cdev)
 654{
 655        struct acpi_device *device = to_acpi_device(cdev->dev->parent);
 656        enum led_brightness brightness = LED_OFF;
 657
 658        if (call_fext_func(device, FUNC_FLAGS, 0x4, 0x0, 0x0) & RADIO_LED_ON)
 659                brightness = LED_FULL;
 660
 661        return brightness;
 662}
 663
 664static int eco_led_set(struct led_classdev *cdev,
 665                       enum led_brightness brightness)
 666{
 667        struct acpi_device *device = to_acpi_device(cdev->dev->parent);
 668        int curr;
 669
 670        curr = call_fext_func(device, FUNC_LEDS, 0x2, ECO_LED, 0x0);
 671        if (brightness >= LED_FULL)
 672                return call_fext_func(device, FUNC_LEDS, 0x1, ECO_LED,
 673                                      curr | ECO_LED_ON);
 674        else
 675                return call_fext_func(device, FUNC_LEDS, 0x1, ECO_LED,
 676                                      curr & ~ECO_LED_ON);
 677}
 678
 679static enum led_brightness eco_led_get(struct led_classdev *cdev)
 680{
 681        struct acpi_device *device = to_acpi_device(cdev->dev->parent);
 682        enum led_brightness brightness = LED_OFF;
 683
 684        if (call_fext_func(device, FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON)
 685                brightness = LED_FULL;
 686
 687        return brightness;
 688}
 689
 690static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
 691{
 692        struct led_classdev *led;
 693        int result;
 694
 695        if (call_fext_func(device,
 696                           FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
 697                led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
 698                if (!led)
 699                        return -ENOMEM;
 700
 701                led->name = "fujitsu::logolamp";
 702                led->brightness_set_blocking = logolamp_set;
 703                led->brightness_get = logolamp_get;
 704                result = devm_led_classdev_register(&device->dev, led);
 705                if (result)
 706                        return result;
 707        }
 708
 709        if ((call_fext_func(device,
 710                            FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) &&
 711            (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) {
 712                led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
 713                if (!led)
 714                        return -ENOMEM;
 715
 716                led->name = "fujitsu::kblamps";
 717                led->brightness_set_blocking = kblamps_set;
 718                led->brightness_get = kblamps_get;
 719                result = devm_led_classdev_register(&device->dev, led);
 720                if (result)
 721                        return result;
 722        }
 723
 724        /*
 725         * BTNI bit 24 seems to indicate the presence of a radio toggle
 726         * button in place of a slide switch, and all such machines appear
 727         * to also have an RF LED.  Therefore use bit 24 as an indicator
 728         * that an RF LED is present.
 729         */
 730        if (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) {
 731                led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
 732                if (!led)
 733                        return -ENOMEM;
 734
 735                led->name = "fujitsu::radio_led";
 736                led->brightness_set_blocking = radio_led_set;
 737                led->brightness_get = radio_led_get;
 738                led->default_trigger = "rfkill-any";
 739                result = devm_led_classdev_register(&device->dev, led);
 740                if (result)
 741                        return result;
 742        }
 743
 744        /* Support for eco led is not always signaled in bit corresponding
 745         * to the bit used to control the led. According to the DSDT table,
 746         * bit 14 seems to indicate presence of said led as well.
 747         * Confirm by testing the status.
 748         */
 749        if ((call_fext_func(device, FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) &&
 750            (call_fext_func(device,
 751                            FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) {
 752                led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
 753                if (!led)
 754                        return -ENOMEM;
 755
 756                led->name = "fujitsu::eco_led";
 757                led->brightness_set_blocking = eco_led_set;
 758                led->brightness_get = eco_led_get;
 759                result = devm_led_classdev_register(&device->dev, led);
 760                if (result)
 761                        return result;
 762        }
 763
 764        return 0;
 765}
 766
 767static int acpi_fujitsu_laptop_add(struct acpi_device *device)
 768{
 769        struct fujitsu_laptop *priv;
 770        int error;
 771        int i;
 772
 773        priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
 774        if (!priv)
 775                return -ENOMEM;
 776
 777        WARN_ONCE(fext, "More than one FUJ02E3 ACPI device was found.  Driver may not work as intended.");
 778        fext = device;
 779
 780        strcpy(acpi_device_name(device), ACPI_FUJITSU_LAPTOP_DEVICE_NAME);
 781        strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS);
 782        device->driver_data = priv;
 783
 784        /* kfifo */
 785        spin_lock_init(&priv->fifo_lock);
 786        error = kfifo_alloc(&priv->fifo, RINGBUFFERSIZE * sizeof(int),
 787                            GFP_KERNEL);
 788        if (error) {
 789                pr_err("kfifo_alloc failed\n");
 790                goto err_stop;
 791        }
 792
 793        error = acpi_fujitsu_laptop_input_setup(device);
 794        if (error)
 795                goto err_free_fifo;
 796
 797        pr_info("ACPI: %s [%s]\n",
 798                acpi_device_name(device), acpi_device_bid(device));
 799
 800        i = 0;
 801        while (call_fext_func(device, FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0
 802                && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE)
 803                ; /* No action, result is discarded */
 804        acpi_handle_debug(device->handle, "Discarded %i ringbuffer entries\n",
 805                          i);
 806
 807        priv->flags_supported = call_fext_func(device, FUNC_FLAGS, 0x0, 0x0,
 808                                               0x0);
 809
 810        /* Make sure our bitmask of supported functions is cleared if the
 811           RFKILL function block is not implemented, like on the S7020. */
 812        if (priv->flags_supported == UNSUPPORTED_CMD)
 813                priv->flags_supported = 0;
 814
 815        if (priv->flags_supported)
 816                priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0,
 817                                                   0x0);
 818
 819        /* Suspect this is a keymap of the application panel, print it */
 820        acpi_handle_info(device->handle, "BTNI: [0x%x]\n",
 821                         call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0));
 822
 823        /* Sync backlight power status */
 824        if (fujitsu_bl && fujitsu_bl->bl_device &&
 825            acpi_video_get_backlight_type() == acpi_backlight_vendor) {
 826                if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3)
 827                        fujitsu_bl->bl_device->props.power = FB_BLANK_POWERDOWN;
 828                else
 829                        fujitsu_bl->bl_device->props.power = FB_BLANK_UNBLANK;
 830        }
 831
 832        error = acpi_fujitsu_laptop_leds_register(device);
 833        if (error)
 834                goto err_free_fifo;
 835
 836        error = fujitsu_laptop_platform_add(device);
 837        if (error)
 838                goto err_free_fifo;
 839
 840        return 0;
 841
 842err_free_fifo:
 843        kfifo_free(&priv->fifo);
 844err_stop:
 845        return error;
 846}
 847
 848static int acpi_fujitsu_laptop_remove(struct acpi_device *device)
 849{
 850        struct fujitsu_laptop *priv = acpi_driver_data(device);
 851
 852        fujitsu_laptop_platform_remove(device);
 853
 854        kfifo_free(&priv->fifo);
 855
 856        return 0;
 857}
 858
 859static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode)
 860{
 861        struct fujitsu_laptop *priv = acpi_driver_data(device);
 862        int status;
 863
 864        status = kfifo_in_locked(&priv->fifo, (unsigned char *)&scancode,
 865                                 sizeof(scancode), &priv->fifo_lock);
 866        if (status != sizeof(scancode)) {
 867                dev_info(&priv->input->dev, "Could not push scancode [0x%x]\n",
 868                         scancode);
 869                return;
 870        }
 871        sparse_keymap_report_event(priv->input, scancode, 1, false);
 872        dev_dbg(&priv->input->dev, "Push scancode into ringbuffer [0x%x]\n",
 873                scancode);
 874}
 875
 876static void acpi_fujitsu_laptop_release(struct acpi_device *device)
 877{
 878        struct fujitsu_laptop *priv = acpi_driver_data(device);
 879        int scancode, status;
 880
 881        while (true) {
 882                status = kfifo_out_locked(&priv->fifo,
 883                                          (unsigned char *)&scancode,
 884                                          sizeof(scancode), &priv->fifo_lock);
 885                if (status != sizeof(scancode))
 886                        return;
 887                sparse_keymap_report_event(priv->input, scancode, 0, false);
 888                dev_dbg(&priv->input->dev,
 889                        "Pop scancode from ringbuffer [0x%x]\n", scancode);
 890        }
 891}
 892
 893static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event)
 894{
 895        struct fujitsu_laptop *priv = acpi_driver_data(device);
 896        int scancode, i = 0;
 897        unsigned int irb;
 898
 899        if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
 900                acpi_handle_info(device->handle, "Unsupported event [0x%x]\n",
 901                                 event);
 902                sparse_keymap_report_event(priv->input, -1, 1, true);
 903                return;
 904        }
 905
 906        if (priv->flags_supported)
 907                priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0,
 908                                                   0x0);
 909
 910        while ((irb = call_fext_func(device,
 911                                     FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 &&
 912               i++ < MAX_HOTKEY_RINGBUFFER_SIZE) {
 913                scancode = irb & 0x4ff;
 914                if (sparse_keymap_entry_from_scancode(priv->input, scancode))
 915                        acpi_fujitsu_laptop_press(device, scancode);
 916                else if (scancode == 0)
 917                        acpi_fujitsu_laptop_release(device);
 918                else
 919                        acpi_handle_info(device->handle,
 920                                         "Unknown GIRB result [%x]\n", irb);
 921        }
 922
 923        /* On some models (first seen on the Skylake-based Lifebook
 924         * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is
 925         * handled in software; its state is queried using FUNC_FLAGS
 926         */
 927        if ((priv->flags_supported & BIT(26)) &&
 928            (call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0) & BIT(26)))
 929                sparse_keymap_report_event(priv->input, BIT(26), 1, true);
 930}
 931
 932/* Initialization */
 933
 934static const struct acpi_device_id fujitsu_bl_device_ids[] = {
 935        {ACPI_FUJITSU_BL_HID, 0},
 936        {"", 0},
 937};
 938
 939static struct acpi_driver acpi_fujitsu_bl_driver = {
 940        .name = ACPI_FUJITSU_BL_DRIVER_NAME,
 941        .class = ACPI_FUJITSU_CLASS,
 942        .ids = fujitsu_bl_device_ids,
 943        .ops = {
 944                .add = acpi_fujitsu_bl_add,
 945                .notify = acpi_fujitsu_bl_notify,
 946                },
 947};
 948
 949static const struct acpi_device_id fujitsu_laptop_device_ids[] = {
 950        {ACPI_FUJITSU_LAPTOP_HID, 0},
 951        {"", 0},
 952};
 953
 954static struct acpi_driver acpi_fujitsu_laptop_driver = {
 955        .name = ACPI_FUJITSU_LAPTOP_DRIVER_NAME,
 956        .class = ACPI_FUJITSU_CLASS,
 957        .ids = fujitsu_laptop_device_ids,
 958        .ops = {
 959                .add = acpi_fujitsu_laptop_add,
 960                .remove = acpi_fujitsu_laptop_remove,
 961                .notify = acpi_fujitsu_laptop_notify,
 962                },
 963};
 964
 965static const struct acpi_device_id fujitsu_ids[] __used = {
 966        {ACPI_FUJITSU_BL_HID, 0},
 967        {ACPI_FUJITSU_LAPTOP_HID, 0},
 968        {"", 0}
 969};
 970MODULE_DEVICE_TABLE(acpi, fujitsu_ids);
 971
 972static int __init fujitsu_init(void)
 973{
 974        int ret;
 975
 976        ret = acpi_bus_register_driver(&acpi_fujitsu_bl_driver);
 977        if (ret)
 978                return ret;
 979
 980        /* Register platform stuff */
 981
 982        ret = platform_driver_register(&fujitsu_pf_driver);
 983        if (ret)
 984                goto err_unregister_acpi;
 985
 986        /* Register laptop driver */
 987
 988        ret = acpi_bus_register_driver(&acpi_fujitsu_laptop_driver);
 989        if (ret)
 990                goto err_unregister_platform_driver;
 991
 992        pr_info("driver " FUJITSU_DRIVER_VERSION " successfully loaded\n");
 993
 994        return 0;
 995
 996err_unregister_platform_driver:
 997        platform_driver_unregister(&fujitsu_pf_driver);
 998err_unregister_acpi:
 999        acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver);
1000
1001        return ret;
1002}
1003
1004static void __exit fujitsu_cleanup(void)
1005{
1006        acpi_bus_unregister_driver(&acpi_fujitsu_laptop_driver);
1007
1008        platform_driver_unregister(&fujitsu_pf_driver);
1009
1010        acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver);
1011
1012        pr_info("driver unloaded\n");
1013}
1014
1015module_init(fujitsu_init);
1016module_exit(fujitsu_cleanup);
1017
1018module_param(use_alt_lcd_levels, int, 0644);
1019MODULE_PARM_DESC(use_alt_lcd_levels, "Interface used for setting LCD brightness level (-1 = auto, 0 = force SBLL, 1 = force SBL2)");
1020module_param(disable_brightness_adjust, bool, 0644);
1021MODULE_PARM_DESC(disable_brightness_adjust, "Disable LCD brightness adjustment");
1022
1023MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon");
1024MODULE_DESCRIPTION("Fujitsu laptop extras support");
1025MODULE_VERSION(FUJITSU_DRIVER_VERSION);
1026MODULE_LICENSE("GPL");
1027