linux/drivers/misc/fujitsu-laptop.c
<<
>>
Prefs
   1/*-*-linux-c-*-*/
   2
   3/*
   4  Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
   5  Copyright (C) 2008 Peter Gruber <nokos@gmx.net>
   6  Based on earlier work:
   7    Copyright (C) 2003 Shane Spencer <shane@bogomip.com>
   8    Adrian Yee <brewt-fujitsu@brewt.org>
   9
  10  Templated from msi-laptop.c and thinkpad_acpi.c which is copyright
  11  by its respective authors.
  12
  13  This program is free software; you can redistribute it and/or modify
  14  it under the terms of the GNU General Public License as published by
  15  the Free Software Foundation; either version 2 of the License, or
  16  (at your option) any later version.
  17
  18  This program is distributed in the hope that it will be useful, but
  19  WITHOUT ANY WARRANTY; without even the implied warranty of
  20  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  21  General Public License for more details.
  22
  23  You should have received a copy of the GNU General Public License
  24  along with this program; if not, write to the Free Software
  25  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  26  02110-1301, USA.
  27 */
  28
  29/*
  30 * fujitsu-laptop.c - Fujitsu laptop support, providing access to additional
  31 * features made available on a range of Fujitsu laptops including the
  32 * P2xxx/P5xxx/S6xxx/S7xxx series.
  33 *
  34 * This driver exports a few files in /sys/devices/platform/fujitsu-laptop/;
  35 * others may be added at a later date.
  36 *
  37 *   lcd_level - Screen brightness: contains a single integer in the
  38 *   range 0..7. (rw)
  39 *
  40 * In addition to these platform device attributes the driver
  41 * registers itself in the Linux backlight control subsystem and is
  42 * available to userspace under /sys/class/backlight/fujitsu-laptop/.
  43 *
  44 * Hotkeys present on certain Fujitsu laptops (eg: the S6xxx series) are
  45 * also supported by this driver.
  46 *
  47 * This driver has been tested on a Fujitsu Lifebook S6410, S7020 and
  48 * P8010.  It should work on most P-series and S-series Lifebooks, but
  49 * YMMV.
  50 *
  51 * The module parameter use_alt_lcd_levels switches between different ACPI
  52 * brightness controls which are used by different Fujitsu laptops.  In most
  53 * cases the correct method is automatically detected. "use_alt_lcd_levels=1"
  54 * is applicable for a Fujitsu Lifebook S6410 if autodetection fails.
  55 *
  56 */
  57
  58#include <linux/module.h>
  59#include <linux/kernel.h>
  60#include <linux/init.h>
  61#include <linux/acpi.h>
  62#include <linux/dmi.h>
  63#include <linux/backlight.h>
  64#include <linux/input.h>
  65#include <linux/kfifo.h>
  66#include <linux/video_output.h>
  67#include <linux/platform_device.h>
  68
  69#define FUJITSU_DRIVER_VERSION "0.4.3"
  70
  71#define FUJITSU_LCD_N_LEVELS 8
  72
  73#define ACPI_FUJITSU_CLASS              "fujitsu"
  74#define ACPI_FUJITSU_HID                "FUJ02B1"
  75#define ACPI_FUJITSU_DRIVER_NAME        "Fujitsu laptop FUJ02B1 ACPI brightness driver"
  76#define ACPI_FUJITSU_DEVICE_NAME        "Fujitsu FUJ02B1"
  77#define ACPI_FUJITSU_HOTKEY_HID         "FUJ02E3"
  78#define ACPI_FUJITSU_HOTKEY_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver"
  79#define ACPI_FUJITSU_HOTKEY_DEVICE_NAME "Fujitsu FUJ02E3"
  80
  81#define ACPI_FUJITSU_NOTIFY_CODE1     0x80
  82
  83#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS     0x86
  84#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS     0x87
  85
  86/* Hotkey details */
  87#define KEY1_CODE       0x410   /* codes for the keys in the GIRB register */
  88#define KEY2_CODE       0x411
  89#define KEY3_CODE       0x412
  90#define KEY4_CODE       0x413
  91
  92#define MAX_HOTKEY_RINGBUFFER_SIZE 100
  93#define RINGBUFFERSIZE 40
  94
  95/* Debugging */
  96#define FUJLAPTOP_LOG      ACPI_FUJITSU_HID ": "
  97#define FUJLAPTOP_ERR      KERN_ERR FUJLAPTOP_LOG
  98#define FUJLAPTOP_NOTICE   KERN_NOTICE FUJLAPTOP_LOG
  99#define FUJLAPTOP_INFO     KERN_INFO FUJLAPTOP_LOG
 100#define FUJLAPTOP_DEBUG    KERN_DEBUG FUJLAPTOP_LOG
 101
 102#define FUJLAPTOP_DBG_ALL         0xffff
 103#define FUJLAPTOP_DBG_ERROR       0x0001
 104#define FUJLAPTOP_DBG_WARN        0x0002
 105#define FUJLAPTOP_DBG_INFO        0x0004
 106#define FUJLAPTOP_DBG_TRACE       0x0008
 107
 108#define dbg_printk(a_dbg_level, format, arg...) \
 109        do { if (dbg_level & a_dbg_level) \
 110                printk(FUJLAPTOP_DEBUG "%s: " format, __func__ , ## arg); \
 111        } while (0)
 112#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
 113#define vdbg_printk(a_dbg_level, format, arg...) \
 114        dbg_printk(a_dbg_level, format, ## arg)
 115#else
 116#define vdbg_printk(a_dbg_level, format, arg...)
 117#endif
 118
 119/* Device controlling the backlight and associated keys */
 120struct fujitsu_t {
 121        acpi_handle acpi_handle;
 122        struct acpi_device *dev;
 123        struct input_dev *input;
 124        char phys[32];
 125        struct backlight_device *bl_device;
 126        struct platform_device *pf_device;
 127        int keycode1, keycode2, keycode3, keycode4;
 128
 129        unsigned int max_brightness;
 130        unsigned int brightness_changed;
 131        unsigned int brightness_level;
 132};
 133
 134static struct fujitsu_t *fujitsu;
 135static int use_alt_lcd_levels = -1;
 136static int disable_brightness_keys = -1;
 137static int disable_brightness_adjust = -1;
 138
 139/* Device used to access other hotkeys on the laptop */
 140struct fujitsu_hotkey_t {
 141        acpi_handle acpi_handle;
 142        struct acpi_device *dev;
 143        struct input_dev *input;
 144        char phys[32];
 145        struct platform_device *pf_device;
 146        struct kfifo *fifo;
 147        spinlock_t fifo_lock;
 148
 149        unsigned int irb;       /* info about the pressed buttons */
 150};
 151
 152static struct fujitsu_hotkey_t *fujitsu_hotkey;
 153
 154static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event,
 155                                       void *data);
 156
 157#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
 158static u32 dbg_level = 0x03;
 159#endif
 160
 161static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data);
 162
 163/* Hardware access for LCD brightness control */
 164
 165static int set_lcd_level(int level)
 166{
 167        acpi_status status = AE_OK;
 168        union acpi_object arg0 = { ACPI_TYPE_INTEGER };
 169        struct acpi_object_list arg_list = { 1, &arg0 };
 170        acpi_handle handle = NULL;
 171
 172        vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBLL [%d]\n",
 173                    level);
 174
 175        if (level < 0 || level >= fujitsu->max_brightness)
 176                return -EINVAL;
 177
 178        if (!fujitsu)
 179                return -EINVAL;
 180
 181        status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle);
 182        if (ACPI_FAILURE(status)) {
 183                vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBLL not present\n");
 184                return -ENODEV;
 185        }
 186
 187        arg0.integer.value = level;
 188
 189        status = acpi_evaluate_object(handle, NULL, &arg_list, NULL);
 190        if (ACPI_FAILURE(status))
 191                return -ENODEV;
 192
 193        return 0;
 194}
 195
 196static int set_lcd_level_alt(int level)
 197{
 198        acpi_status status = AE_OK;
 199        union acpi_object arg0 = { ACPI_TYPE_INTEGER };
 200        struct acpi_object_list arg_list = { 1, &arg0 };
 201        acpi_handle handle = NULL;
 202
 203        vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBL2 [%d]\n",
 204                    level);
 205
 206        if (level < 0 || level >= fujitsu->max_brightness)
 207                return -EINVAL;
 208
 209        if (!fujitsu)
 210                return -EINVAL;
 211
 212        status = acpi_get_handle(fujitsu->acpi_handle, "SBL2", &handle);
 213        if (ACPI_FAILURE(status)) {
 214                vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBL2 not present\n");
 215                return -ENODEV;
 216        }
 217
 218        arg0.integer.value = level;
 219
 220        status = acpi_evaluate_object(handle, NULL, &arg_list, NULL);
 221        if (ACPI_FAILURE(status))
 222                return -ENODEV;
 223
 224        return 0;
 225}
 226
 227static int get_lcd_level(void)
 228{
 229        unsigned long long state = 0;
 230        acpi_status status = AE_OK;
 231
 232        vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLL\n");
 233
 234        status =
 235            acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state);
 236        if (status < 0)
 237                return status;
 238
 239        fujitsu->brightness_level = state & 0x0fffffff;
 240
 241        if (state & 0x80000000)
 242                fujitsu->brightness_changed = 1;
 243        else
 244                fujitsu->brightness_changed = 0;
 245
 246        return fujitsu->brightness_level;
 247}
 248
 249static int get_max_brightness(void)
 250{
 251        unsigned long long state = 0;
 252        acpi_status status = AE_OK;
 253
 254        vdbg_printk(FUJLAPTOP_DBG_TRACE, "get max lcd level via RBLL\n");
 255
 256        status =
 257            acpi_evaluate_integer(fujitsu->acpi_handle, "RBLL", NULL, &state);
 258        if (status < 0)
 259                return status;
 260
 261        fujitsu->max_brightness = state;
 262
 263        return fujitsu->max_brightness;
 264}
 265
 266static int get_lcd_level_alt(void)
 267{
 268        unsigned long long state = 0;
 269        acpi_status status = AE_OK;
 270
 271        vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLS\n");
 272
 273        status =
 274            acpi_evaluate_integer(fujitsu->acpi_handle, "GBLS", NULL, &state);
 275        if (status < 0)
 276                return status;
 277
 278        fujitsu->brightness_level = state & 0x0fffffff;
 279
 280        if (state & 0x80000000)
 281                fujitsu->brightness_changed = 1;
 282        else
 283                fujitsu->brightness_changed = 0;
 284
 285        return fujitsu->brightness_level;
 286}
 287
 288/* Backlight device stuff */
 289
 290static int bl_get_brightness(struct backlight_device *b)
 291{
 292        if (use_alt_lcd_levels)
 293                return get_lcd_level_alt();
 294        else
 295                return get_lcd_level();
 296}
 297
 298static int bl_update_status(struct backlight_device *b)
 299{
 300        if (use_alt_lcd_levels)
 301                return set_lcd_level_alt(b->props.brightness);
 302        else
 303                return set_lcd_level(b->props.brightness);
 304}
 305
 306static struct backlight_ops fujitsubl_ops = {
 307        .get_brightness = bl_get_brightness,
 308        .update_status = bl_update_status,
 309};
 310
 311/* Platform LCD brightness device */
 312
 313static ssize_t
 314show_max_brightness(struct device *dev,
 315                    struct device_attribute *attr, char *buf)
 316{
 317
 318        int ret;
 319
 320        ret = get_max_brightness();
 321        if (ret < 0)
 322                return ret;
 323
 324        return sprintf(buf, "%i\n", ret);
 325}
 326
 327static ssize_t
 328show_brightness_changed(struct device *dev,
 329                        struct device_attribute *attr, char *buf)
 330{
 331
 332        int ret;
 333
 334        ret = fujitsu->brightness_changed;
 335        if (ret < 0)
 336                return ret;
 337
 338        return sprintf(buf, "%i\n", ret);
 339}
 340
 341static ssize_t show_lcd_level(struct device *dev,
 342                              struct device_attribute *attr, char *buf)
 343{
 344
 345        int ret;
 346
 347        if (use_alt_lcd_levels)
 348                ret = get_lcd_level_alt();
 349        else
 350                ret = get_lcd_level();
 351        if (ret < 0)
 352                return ret;
 353
 354        return sprintf(buf, "%i\n", ret);
 355}
 356
 357static ssize_t store_lcd_level(struct device *dev,
 358                               struct device_attribute *attr, const char *buf,
 359                               size_t count)
 360{
 361
 362        int level, ret;
 363
 364        if (sscanf(buf, "%i", &level) != 1
 365            || (level < 0 || level >= fujitsu->max_brightness))
 366                return -EINVAL;
 367
 368        if (use_alt_lcd_levels)
 369                ret = set_lcd_level_alt(level);
 370        else
 371                ret = set_lcd_level(level);
 372        if (ret < 0)
 373                return ret;
 374
 375        if (use_alt_lcd_levels)
 376                ret = get_lcd_level_alt();
 377        else
 378                ret = get_lcd_level();
 379        if (ret < 0)
 380                return ret;
 381
 382        return count;
 383}
 384
 385/* Hardware access for hotkey device */
 386
 387static int get_irb(void)
 388{
 389        unsigned long long state = 0;
 390        acpi_status status = AE_OK;
 391
 392        vdbg_printk(FUJLAPTOP_DBG_TRACE, "Get irb\n");
 393
 394        status =
 395            acpi_evaluate_integer(fujitsu_hotkey->acpi_handle, "GIRB", NULL,
 396                                  &state);
 397        if (status < 0)
 398                return status;
 399
 400        fujitsu_hotkey->irb = state;
 401
 402        return fujitsu_hotkey->irb;
 403}
 404
 405static ssize_t
 406ignore_store(struct device *dev,
 407             struct device_attribute *attr, const char *buf, size_t count)
 408{
 409        return count;
 410}
 411
 412static DEVICE_ATTR(max_brightness, 0444, show_max_brightness, ignore_store);
 413static DEVICE_ATTR(brightness_changed, 0444, show_brightness_changed,
 414                   ignore_store);
 415static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
 416
 417static struct attribute *fujitsupf_attributes[] = {
 418        &dev_attr_brightness_changed.attr,
 419        &dev_attr_max_brightness.attr,
 420        &dev_attr_lcd_level.attr,
 421        NULL
 422};
 423
 424static struct attribute_group fujitsupf_attribute_group = {
 425        .attrs = fujitsupf_attributes
 426};
 427
 428static struct platform_driver fujitsupf_driver = {
 429        .driver = {
 430                   .name = "fujitsu-laptop",
 431                   .owner = THIS_MODULE,
 432                   }
 433};
 434
 435static void dmi_check_cb_common(const struct dmi_system_id *id)
 436{
 437        acpi_handle handle;
 438        int have_blnf;
 439        printk(KERN_INFO "fujitsu-laptop: Identified laptop model '%s'.\n",
 440               id->ident);
 441        have_blnf = ACPI_SUCCESS
 442            (acpi_get_handle(NULL, "\\_SB.PCI0.GFX0.LCD.BLNF", &handle));
 443        if (use_alt_lcd_levels == -1) {
 444                vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detecting usealt\n");
 445                use_alt_lcd_levels = 1;
 446        }
 447        if (disable_brightness_keys == -1) {
 448                vdbg_printk(FUJLAPTOP_DBG_TRACE,
 449                            "auto-detecting disable_keys\n");
 450                disable_brightness_keys = have_blnf ? 1 : 0;
 451        }
 452        if (disable_brightness_adjust == -1) {
 453                vdbg_printk(FUJLAPTOP_DBG_TRACE,
 454                            "auto-detecting disable_adjust\n");
 455                disable_brightness_adjust = have_blnf ? 0 : 1;
 456        }
 457}
 458
 459static int dmi_check_cb_s6410(const struct dmi_system_id *id)
 460{
 461        dmi_check_cb_common(id);
 462        fujitsu->keycode1 = KEY_SCREENLOCK;     /* "Lock" */
 463        fujitsu->keycode2 = KEY_HELP;   /* "Mobility Center" */
 464        return 0;
 465}
 466
 467static int dmi_check_cb_s6420(const struct dmi_system_id *id)
 468{
 469        dmi_check_cb_common(id);
 470        fujitsu->keycode1 = KEY_SCREENLOCK;     /* "Lock" */
 471        fujitsu->keycode2 = KEY_HELP;   /* "Mobility Center" */
 472        return 0;
 473}
 474
 475static int dmi_check_cb_p8010(const struct dmi_system_id *id)
 476{
 477        dmi_check_cb_common(id);
 478        fujitsu->keycode1 = KEY_HELP;   /* "Support" */
 479        fujitsu->keycode3 = KEY_SWITCHVIDEOMODE;        /* "Presentation" */
 480        fujitsu->keycode4 = KEY_WWW;    /* "Internet" */
 481        return 0;
 482}
 483
 484static struct dmi_system_id fujitsu_dmi_table[] = {
 485        {
 486         .ident = "Fujitsu Siemens S6410",
 487         .matches = {
 488                     DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
 489                     DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"),
 490                     },
 491         .callback = dmi_check_cb_s6410},
 492        {
 493         .ident = "Fujitsu Siemens S6420",
 494         .matches = {
 495                     DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
 496                     DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"),
 497                     },
 498         .callback = dmi_check_cb_s6420},
 499        {
 500         .ident = "Fujitsu LifeBook P8010",
 501         .matches = {
 502                     DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
 503                     DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P8010"),
 504                     },
 505         .callback = dmi_check_cb_p8010},
 506        {}
 507};
 508
 509/* ACPI device for LCD brightness control */
 510
 511static int acpi_fujitsu_add(struct acpi_device *device)
 512{
 513        acpi_status status;
 514        acpi_handle handle;
 515        int result = 0;
 516        int state = 0;
 517        struct input_dev *input;
 518        int error;
 519
 520        if (!device)
 521                return -EINVAL;
 522
 523        fujitsu->acpi_handle = device->handle;
 524        sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_DEVICE_NAME);
 525        sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
 526        device->driver_data = fujitsu;
 527
 528        status = acpi_install_notify_handler(device->handle,
 529                                             ACPI_DEVICE_NOTIFY,
 530                                             acpi_fujitsu_notify, fujitsu);
 531
 532        if (ACPI_FAILURE(status)) {
 533                printk(KERN_ERR "Error installing notify handler\n");
 534                error = -ENODEV;
 535                goto err_stop;
 536        }
 537
 538        fujitsu->input = input = input_allocate_device();
 539        if (!input) {
 540                error = -ENOMEM;
 541                goto err_uninstall_notify;
 542        }
 543
 544        snprintf(fujitsu->phys, sizeof(fujitsu->phys),
 545                 "%s/video/input0", acpi_device_hid(device));
 546
 547        input->name = acpi_device_name(device);
 548        input->phys = fujitsu->phys;
 549        input->id.bustype = BUS_HOST;
 550        input->id.product = 0x06;
 551        input->dev.parent = &device->dev;
 552        input->evbit[0] = BIT(EV_KEY);
 553        set_bit(KEY_BRIGHTNESSUP, input->keybit);
 554        set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
 555        set_bit(KEY_UNKNOWN, input->keybit);
 556
 557        error = input_register_device(input);
 558        if (error)
 559                goto err_free_input_dev;
 560
 561        result = acpi_bus_get_power(fujitsu->acpi_handle, &state);
 562        if (result) {
 563                printk(KERN_ERR "Error reading power state\n");
 564                goto end;
 565        }
 566
 567        printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
 568               acpi_device_name(device), acpi_device_bid(device),
 569               !device->power.state ? "on" : "off");
 570
 571        fujitsu->dev = device;
 572
 573        if (ACPI_SUCCESS
 574            (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) {
 575                vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
 576                if (ACPI_FAILURE
 577                    (acpi_evaluate_object
 578                     (device->handle, METHOD_NAME__INI, NULL, NULL)))
 579                        printk(KERN_ERR "_INI Method failed\n");
 580        }
 581
 582        /* do config (detect defaults) */
 583        use_alt_lcd_levels = use_alt_lcd_levels == 1 ? 1 : 0;
 584        disable_brightness_keys = disable_brightness_keys == 1 ? 1 : 0;
 585        disable_brightness_adjust = disable_brightness_adjust == 1 ? 1 : 0;
 586        vdbg_printk(FUJLAPTOP_DBG_INFO,
 587                    "config: [alt interface: %d], [key disable: %d], [adjust disable: %d]\n",
 588                    use_alt_lcd_levels, disable_brightness_keys,
 589                    disable_brightness_adjust);
 590
 591        if (get_max_brightness() <= 0)
 592                fujitsu->max_brightness = FUJITSU_LCD_N_LEVELS;
 593        if (use_alt_lcd_levels)
 594                get_lcd_level_alt();
 595        else
 596                get_lcd_level();
 597
 598        return result;
 599
 600end:
 601err_free_input_dev:
 602        input_free_device(input);
 603err_uninstall_notify:
 604        acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
 605                                   acpi_fujitsu_notify);
 606err_stop:
 607
 608        return result;
 609}
 610
 611static int acpi_fujitsu_remove(struct acpi_device *device, int type)
 612{
 613        acpi_status status;
 614        struct fujitsu_t *fujitsu = NULL;
 615
 616        if (!device || !acpi_driver_data(device))
 617                return -EINVAL;
 618
 619        fujitsu = acpi_driver_data(device);
 620
 621        status = acpi_remove_notify_handler(fujitsu->acpi_handle,
 622                                            ACPI_DEVICE_NOTIFY,
 623                                            acpi_fujitsu_notify);
 624
 625        if (!device || !acpi_driver_data(device))
 626                return -EINVAL;
 627
 628        fujitsu->acpi_handle = NULL;
 629
 630        return 0;
 631}
 632
 633/* Brightness notify */
 634
 635static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data)
 636{
 637        struct input_dev *input;
 638        int keycode;
 639        int oldb, newb;
 640
 641        input = fujitsu->input;
 642
 643        switch (event) {
 644        case ACPI_FUJITSU_NOTIFY_CODE1:
 645                keycode = 0;
 646                oldb = fujitsu->brightness_level;
 647                get_lcd_level();  /* the alt version always yields changed */
 648                newb = fujitsu->brightness_level;
 649
 650                vdbg_printk(FUJLAPTOP_DBG_TRACE,
 651                            "brightness button event [%i -> %i (%i)]\n",
 652                            oldb, newb, fujitsu->brightness_changed);
 653
 654                if (oldb == newb && fujitsu->brightness_changed) {
 655                        keycode = 0;
 656                        if (disable_brightness_keys != 1) {
 657                                if (oldb == 0) {
 658                                        acpi_bus_generate_proc_event
 659                                            (fujitsu->dev,
 660                                             ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS,
 661                                             0);
 662                                        keycode = KEY_BRIGHTNESSDOWN;
 663                                } else if (oldb ==
 664                                           (fujitsu->max_brightness) - 1) {
 665                                        acpi_bus_generate_proc_event
 666                                            (fujitsu->dev,
 667                                             ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS,
 668                                             0);
 669                                        keycode = KEY_BRIGHTNESSUP;
 670                                }
 671                        }
 672                } else if (oldb < newb) {
 673                        if (disable_brightness_adjust != 1) {
 674                                if (use_alt_lcd_levels)
 675                                        set_lcd_level_alt(newb);
 676                                else
 677                                        set_lcd_level(newb);
 678                        }
 679                        if (disable_brightness_keys != 1) {
 680                                acpi_bus_generate_proc_event(fujitsu->dev,
 681                                        ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS, 0);
 682                                keycode = KEY_BRIGHTNESSUP;
 683                        }
 684                } else if (oldb > newb) {
 685                        if (disable_brightness_adjust != 1) {
 686                                if (use_alt_lcd_levels)
 687                                        set_lcd_level_alt(newb);
 688                                else
 689                                        set_lcd_level(newb);
 690                        }
 691                        if (disable_brightness_keys != 1) {
 692                                acpi_bus_generate_proc_event(fujitsu->dev,
 693                                        ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS, 0);
 694                                keycode = KEY_BRIGHTNESSDOWN;
 695                        }
 696                } else {
 697                        keycode = KEY_UNKNOWN;
 698                }
 699                break;
 700        default:
 701                keycode = KEY_UNKNOWN;
 702                vdbg_printk(FUJLAPTOP_DBG_WARN,
 703                            "unsupported event [0x%x]\n", event);
 704                break;
 705        }
 706
 707        if (keycode != 0) {
 708                input_report_key(input, keycode, 1);
 709                input_sync(input);
 710                input_report_key(input, keycode, 0);
 711                input_sync(input);
 712        }
 713
 714        return;
 715}
 716
 717/* ACPI device for hotkey handling */
 718
 719static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
 720{
 721        acpi_status status;
 722        acpi_handle handle;
 723        int result = 0;
 724        int state = 0;
 725        struct input_dev *input;
 726        int error;
 727        int i;
 728
 729        if (!device)
 730                return -EINVAL;
 731
 732        fujitsu_hotkey->acpi_handle = device->handle;
 733        sprintf(acpi_device_name(device), "%s",
 734                ACPI_FUJITSU_HOTKEY_DEVICE_NAME);
 735        sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
 736        device->driver_data = fujitsu_hotkey;
 737
 738        status = acpi_install_notify_handler(device->handle,
 739                                             ACPI_DEVICE_NOTIFY,
 740                                             acpi_fujitsu_hotkey_notify,
 741                                             fujitsu_hotkey);
 742
 743        if (ACPI_FAILURE(status)) {
 744                printk(KERN_ERR "Error installing notify handler\n");
 745                error = -ENODEV;
 746                goto err_stop;
 747        }
 748
 749        /* kfifo */
 750        spin_lock_init(&fujitsu_hotkey->fifo_lock);
 751        fujitsu_hotkey->fifo =
 752            kfifo_alloc(RINGBUFFERSIZE * sizeof(int), GFP_KERNEL,
 753                        &fujitsu_hotkey->fifo_lock);
 754        if (IS_ERR(fujitsu_hotkey->fifo)) {
 755                printk(KERN_ERR "kfifo_alloc failed\n");
 756                error = PTR_ERR(fujitsu_hotkey->fifo);
 757                goto err_stop;
 758        }
 759
 760        fujitsu_hotkey->input = input = input_allocate_device();
 761        if (!input) {
 762                error = -ENOMEM;
 763                goto err_uninstall_notify;
 764        }
 765
 766        snprintf(fujitsu_hotkey->phys, sizeof(fujitsu_hotkey->phys),
 767                 "%s/video/input0", acpi_device_hid(device));
 768
 769        input->name = acpi_device_name(device);
 770        input->phys = fujitsu_hotkey->phys;
 771        input->id.bustype = BUS_HOST;
 772        input->id.product = 0x06;
 773        input->dev.parent = &device->dev;
 774        input->evbit[0] = BIT(EV_KEY);
 775        set_bit(fujitsu->keycode1, input->keybit);
 776        set_bit(fujitsu->keycode2, input->keybit);
 777        set_bit(fujitsu->keycode3, input->keybit);
 778        set_bit(fujitsu->keycode4, input->keybit);
 779        set_bit(KEY_UNKNOWN, input->keybit);
 780
 781        error = input_register_device(input);
 782        if (error)
 783                goto err_free_input_dev;
 784
 785        result = acpi_bus_get_power(fujitsu_hotkey->acpi_handle, &state);
 786        if (result) {
 787                printk(KERN_ERR "Error reading power state\n");
 788                goto end;
 789        }
 790
 791        printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
 792               acpi_device_name(device), acpi_device_bid(device),
 793               !device->power.state ? "on" : "off");
 794
 795        fujitsu_hotkey->dev = device;
 796
 797        if (ACPI_SUCCESS
 798            (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) {
 799                vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
 800                if (ACPI_FAILURE
 801                    (acpi_evaluate_object
 802                     (device->handle, METHOD_NAME__INI, NULL, NULL)))
 803                        printk(KERN_ERR "_INI Method failed\n");
 804        }
 805
 806        i = 0;                  /* Discard hotkey ringbuffer */
 807        while (get_irb() != 0 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) ;
 808        vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i);
 809
 810        return result;
 811
 812end:
 813err_free_input_dev:
 814        input_free_device(input);
 815err_uninstall_notify:
 816        acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
 817                                   acpi_fujitsu_hotkey_notify);
 818        kfifo_free(fujitsu_hotkey->fifo);
 819err_stop:
 820
 821        return result;
 822}
 823
 824static int acpi_fujitsu_hotkey_remove(struct acpi_device *device, int type)
 825{
 826        acpi_status status;
 827        struct fujitsu_hotkey_t *fujitsu_hotkey = NULL;
 828
 829        if (!device || !acpi_driver_data(device))
 830                return -EINVAL;
 831
 832        fujitsu_hotkey = acpi_driver_data(device);
 833
 834        status = acpi_remove_notify_handler(fujitsu_hotkey->acpi_handle,
 835                                            ACPI_DEVICE_NOTIFY,
 836                                            acpi_fujitsu_hotkey_notify);
 837
 838        fujitsu_hotkey->acpi_handle = NULL;
 839
 840        kfifo_free(fujitsu_hotkey->fifo);
 841
 842        return 0;
 843}
 844
 845static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event,
 846                                       void *data)
 847{
 848        struct input_dev *input;
 849        int keycode, keycode_r;
 850        unsigned int irb = 1;
 851        int i, status;
 852
 853        input = fujitsu_hotkey->input;
 854
 855        vdbg_printk(FUJLAPTOP_DBG_TRACE, "Hotkey event\n");
 856
 857        switch (event) {
 858        case ACPI_FUJITSU_NOTIFY_CODE1:
 859                i = 0;
 860                while ((irb = get_irb()) != 0
 861                       && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) {
 862                        vdbg_printk(FUJLAPTOP_DBG_TRACE, "GIRB result [%x]\n",
 863                                    irb);
 864
 865                        switch (irb & 0x4ff) {
 866                        case KEY1_CODE:
 867                                keycode = fujitsu->keycode1;
 868                                break;
 869                        case KEY2_CODE:
 870                                keycode = fujitsu->keycode2;
 871                                break;
 872                        case KEY3_CODE:
 873                                keycode = fujitsu->keycode3;
 874                                break;
 875                        case KEY4_CODE:
 876                                keycode = fujitsu->keycode4;
 877                                break;
 878                        case 0:
 879                                keycode = 0;
 880                                break;
 881                        default:
 882                                vdbg_printk(FUJLAPTOP_DBG_WARN,
 883                                            "Unknown GIRB result [%x]\n", irb);
 884                                keycode = -1;
 885                                break;
 886                        }
 887                        if (keycode > 0) {
 888                                vdbg_printk(FUJLAPTOP_DBG_TRACE,
 889                                        "Push keycode into ringbuffer [%d]\n",
 890                                        keycode);
 891                                status = kfifo_put(fujitsu_hotkey->fifo,
 892                                                   (unsigned char *)&keycode,
 893                                                   sizeof(keycode));
 894                                if (status != sizeof(keycode)) {
 895                                        vdbg_printk(FUJLAPTOP_DBG_WARN,
 896                                            "Could not push keycode [0x%x]\n",
 897                                            keycode);
 898                                } else {
 899                                        input_report_key(input, keycode, 1);
 900                                        input_sync(input);
 901                                }
 902                        } else if (keycode == 0) {
 903                                while ((status =
 904                                        kfifo_get
 905                                        (fujitsu_hotkey->fifo, (unsigned char *)
 906                                         &keycode_r,
 907                                         sizeof
 908                                         (keycode_r))) == sizeof(keycode_r)) {
 909                                        input_report_key(input, keycode_r, 0);
 910                                        input_sync(input);
 911                                        vdbg_printk(FUJLAPTOP_DBG_TRACE,
 912                                          "Pop keycode from ringbuffer [%d]\n",
 913                                          keycode_r);
 914                                }
 915                        }
 916                }
 917
 918                break;
 919        default:
 920                keycode = KEY_UNKNOWN;
 921                vdbg_printk(FUJLAPTOP_DBG_WARN,
 922                            "Unsupported event [0x%x]\n", event);
 923                input_report_key(input, keycode, 1);
 924                input_sync(input);
 925                input_report_key(input, keycode, 0);
 926                input_sync(input);
 927                break;
 928        }
 929
 930        return;
 931}
 932
 933/* Initialization */
 934
 935static const struct acpi_device_id fujitsu_device_ids[] = {
 936        {ACPI_FUJITSU_HID, 0},
 937        {"", 0},
 938};
 939
 940static struct acpi_driver acpi_fujitsu_driver = {
 941        .name = ACPI_FUJITSU_DRIVER_NAME,
 942        .class = ACPI_FUJITSU_CLASS,
 943        .ids = fujitsu_device_ids,
 944        .ops = {
 945                .add = acpi_fujitsu_add,
 946                .remove = acpi_fujitsu_remove,
 947                },
 948};
 949
 950static const struct acpi_device_id fujitsu_hotkey_device_ids[] = {
 951        {ACPI_FUJITSU_HOTKEY_HID, 0},
 952        {"", 0},
 953};
 954
 955static struct acpi_driver acpi_fujitsu_hotkey_driver = {
 956        .name = ACPI_FUJITSU_HOTKEY_DRIVER_NAME,
 957        .class = ACPI_FUJITSU_CLASS,
 958        .ids = fujitsu_hotkey_device_ids,
 959        .ops = {
 960                .add = acpi_fujitsu_hotkey_add,
 961                .remove = acpi_fujitsu_hotkey_remove,
 962                },
 963};
 964
 965static int __init fujitsu_init(void)
 966{
 967        int ret, result, max_brightness;
 968
 969        if (acpi_disabled)
 970                return -ENODEV;
 971
 972        fujitsu = kmalloc(sizeof(struct fujitsu_t), GFP_KERNEL);
 973        if (!fujitsu)
 974                return -ENOMEM;
 975        memset(fujitsu, 0, sizeof(struct fujitsu_t));
 976        fujitsu->keycode1 = KEY_PROG1;
 977        fujitsu->keycode2 = KEY_PROG2;
 978        fujitsu->keycode3 = KEY_PROG3;
 979        fujitsu->keycode4 = KEY_PROG4;
 980        dmi_check_system(fujitsu_dmi_table);
 981
 982        result = acpi_bus_register_driver(&acpi_fujitsu_driver);
 983        if (result < 0) {
 984                ret = -ENODEV;
 985                goto fail_acpi;
 986        }
 987
 988        /* Register platform stuff */
 989
 990        fujitsu->pf_device = platform_device_alloc("fujitsu-laptop", -1);
 991        if (!fujitsu->pf_device) {
 992                ret = -ENOMEM;
 993                goto fail_platform_driver;
 994        }
 995
 996        ret = platform_device_add(fujitsu->pf_device);
 997        if (ret)
 998                goto fail_platform_device1;
 999
1000        ret =
1001            sysfs_create_group(&fujitsu->pf_device->dev.kobj,
1002                               &fujitsupf_attribute_group);
1003        if (ret)
1004                goto fail_platform_device2;
1005
1006        /* Register backlight stuff */
1007
1008        if (!acpi_video_backlight_support()) {
1009                fujitsu->bl_device =
1010                        backlight_device_register("fujitsu-laptop", NULL, NULL,
1011                                                  &fujitsubl_ops);
1012                if (IS_ERR(fujitsu->bl_device))
1013                        return PTR_ERR(fujitsu->bl_device);
1014                max_brightness = fujitsu->max_brightness;
1015                fujitsu->bl_device->props.max_brightness = max_brightness - 1;
1016                fujitsu->bl_device->props.brightness = fujitsu->brightness_level;
1017        }
1018
1019        ret = platform_driver_register(&fujitsupf_driver);
1020        if (ret)
1021                goto fail_backlight;
1022
1023        /* Register hotkey driver */
1024
1025        fujitsu_hotkey = kmalloc(sizeof(struct fujitsu_hotkey_t), GFP_KERNEL);
1026        if (!fujitsu_hotkey) {
1027                ret = -ENOMEM;
1028                goto fail_hotkey;
1029        }
1030        memset(fujitsu_hotkey, 0, sizeof(struct fujitsu_hotkey_t));
1031
1032        result = acpi_bus_register_driver(&acpi_fujitsu_hotkey_driver);
1033        if (result < 0) {
1034                ret = -ENODEV;
1035                goto fail_hotkey1;
1036        }
1037
1038        printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION
1039               " successfully loaded.\n");
1040
1041        return 0;
1042
1043fail_hotkey1:
1044
1045        kfree(fujitsu_hotkey);
1046
1047fail_hotkey:
1048
1049        platform_driver_unregister(&fujitsupf_driver);
1050
1051fail_backlight:
1052
1053        if (fujitsu->bl_device)
1054                backlight_device_unregister(fujitsu->bl_device);
1055
1056fail_platform_device2:
1057
1058        platform_device_del(fujitsu->pf_device);
1059
1060fail_platform_device1:
1061
1062        platform_device_put(fujitsu->pf_device);
1063
1064fail_platform_driver:
1065
1066        acpi_bus_unregister_driver(&acpi_fujitsu_driver);
1067
1068fail_acpi:
1069
1070        kfree(fujitsu);
1071
1072        return ret;
1073}
1074
1075static void __exit fujitsu_cleanup(void)
1076{
1077        sysfs_remove_group(&fujitsu->pf_device->dev.kobj,
1078                           &fujitsupf_attribute_group);
1079        platform_device_unregister(fujitsu->pf_device);
1080        platform_driver_unregister(&fujitsupf_driver);
1081        if (fujitsu->bl_device)
1082                backlight_device_unregister(fujitsu->bl_device);
1083
1084        acpi_bus_unregister_driver(&acpi_fujitsu_driver);
1085
1086        kfree(fujitsu);
1087
1088        acpi_bus_unregister_driver(&acpi_fujitsu_hotkey_driver);
1089
1090        kfree(fujitsu_hotkey);
1091
1092        printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n");
1093}
1094
1095module_init(fujitsu_init);
1096module_exit(fujitsu_cleanup);
1097
1098module_param(use_alt_lcd_levels, uint, 0644);
1099MODULE_PARM_DESC(use_alt_lcd_levels,
1100                 "Use alternative interface for lcd_levels (needed for Lifebook s6410).");
1101module_param(disable_brightness_keys, uint, 0644);
1102MODULE_PARM_DESC(disable_brightness_keys,
1103                 "Disable brightness keys (eg. if they are already handled by the generic ACPI_VIDEO device).");
1104module_param(disable_brightness_adjust, uint, 0644);
1105MODULE_PARM_DESC(disable_brightness_adjust, "Disable brightness adjustment .");
1106#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
1107module_param_named(debug, dbg_level, uint, 0644);
1108MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
1109#endif
1110
1111MODULE_AUTHOR("Jonathan Woithe, Peter Gruber");
1112MODULE_DESCRIPTION("Fujitsu laptop extras support");
1113MODULE_VERSION(FUJITSU_DRIVER_VERSION);
1114MODULE_LICENSE("GPL");
1115
1116MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*");
1117MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*");
1118
1119static struct pnp_device_id pnp_ids[] = {
1120        {.id = "FUJ02bf"},
1121        {.id = "FUJ02B1"},
1122        {.id = "FUJ02E3"},
1123        {.id = ""}
1124};
1125
1126MODULE_DEVICE_TABLE(pnp, pnp_ids);
1127