linux/drivers/platform/x86/fujitsu-tablet.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2006-2012 Robert Gerlach <khnz@gmx.de>
   3 * Copyright (C) 2005-2006 Jan Rychter <jan@rychter.com>
   4 *
   5 * You can redistribute and/or modify this program under the terms of the
   6 * GNU General Public License version 2 as published by the Free Software
   7 * Foundation.
   8 *
   9 * This program is distributed in the hope that it will be useful, but
  10 * WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
  12 * Public License for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License along
  15 * with this program; if not, write to the Free Software Foundation, Inc.,
  16 * 59 Temple Place Suite 330, Boston, MA 02111-1307, USA.
  17 */
  18
  19#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  20
  21#include <linux/kernel.h>
  22#include <linux/module.h>
  23#include <linux/init.h>
  24#include <linux/bitops.h>
  25#include <linux/io.h>
  26#include <linux/ioport.h>
  27#include <linux/acpi.h>
  28#include <linux/device.h>
  29#include <linux/interrupt.h>
  30#include <linux/input.h>
  31#include <linux/delay.h>
  32#include <linux/dmi.h>
  33
  34#define MODULENAME "fujitsu-tablet"
  35
  36#define ACPI_FUJITSU_CLASS "fujitsu"
  37
  38#define INVERT_TABLET_MODE_BIT      0x01
  39#define INVERT_DOCK_STATE_BIT       0x02
  40#define FORCE_TABLET_MODE_IF_UNDOCK 0x04
  41
  42#define KEYMAP_LEN 16
  43
  44static const struct acpi_device_id fujitsu_ids[] = {
  45        { .id = "FUJ02BD" },
  46        { .id = "FUJ02BF" },
  47        { .id = "" }
  48};
  49
  50struct fujitsu_config {
  51        unsigned short keymap[KEYMAP_LEN];
  52        unsigned int quirks;
  53};
  54
  55static unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initdata = {
  56        KEY_RESERVED,
  57        KEY_RESERVED,
  58        KEY_RESERVED,
  59        KEY_RESERVED,
  60        KEY_SCROLLDOWN,
  61        KEY_SCROLLUP,
  62        KEY_ROTATE_DISPLAY,
  63        KEY_LEFTCTRL,
  64        KEY_BRIGHTNESSUP,
  65        KEY_BRIGHTNESSDOWN,
  66        KEY_BRIGHTNESS_ZERO,
  67        KEY_RESERVED,
  68        KEY_RESERVED,
  69        KEY_RESERVED,
  70        KEY_RESERVED,
  71        KEY_LEFTALT
  72};
  73
  74static unsigned short keymap_Lifebook_T901[KEYMAP_LEN] __initdata = {
  75        KEY_RESERVED,
  76        KEY_RESERVED,
  77        KEY_RESERVED,
  78        KEY_RESERVED,
  79        KEY_SCROLLDOWN,
  80        KEY_SCROLLUP,
  81        KEY_CYCLEWINDOWS,
  82        KEY_LEFTCTRL,
  83        KEY_RESERVED,
  84        KEY_RESERVED,
  85        KEY_RESERVED,
  86        KEY_RESERVED,
  87        KEY_RESERVED,
  88        KEY_RESERVED,
  89        KEY_RESERVED,
  90        KEY_LEFTMETA
  91};
  92
  93static unsigned short keymap_Lifebook_T902[KEYMAP_LEN] __initdata = {
  94        KEY_RESERVED,
  95        KEY_VOLUMEDOWN,
  96        KEY_VOLUMEUP,
  97        KEY_CYCLEWINDOWS,
  98        KEY_PROG1,
  99        KEY_PROG2,
 100        KEY_LEFTMETA,
 101        KEY_RESERVED,
 102        KEY_RESERVED,
 103        KEY_RESERVED,
 104        KEY_RESERVED,
 105        KEY_RESERVED,
 106        KEY_RESERVED,
 107        KEY_RESERVED,
 108        KEY_RESERVED,
 109        KEY_RESERVED,
 110};
 111
 112static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initdata = {
 113        KEY_RESERVED,
 114        KEY_RESERVED,
 115        KEY_RESERVED,
 116        KEY_RESERVED,
 117        KEY_PROG1,
 118        KEY_PROG2,
 119        KEY_ROTATE_DISPLAY,
 120        KEY_RESERVED,
 121        KEY_RESERVED,
 122        KEY_RESERVED,
 123        KEY_UP,
 124        KEY_DOWN,
 125        KEY_RESERVED,
 126        KEY_RESERVED,
 127        KEY_LEFTCTRL,
 128        KEY_LEFTALT
 129};
 130
 131static unsigned short keymap_Stylistic_Tseries[KEYMAP_LEN] __initdata = {
 132        KEY_RESERVED,
 133        KEY_RESERVED,
 134        KEY_RESERVED,
 135        KEY_RESERVED,
 136        KEY_PRINT,
 137        KEY_BACKSPACE,
 138        KEY_SPACE,
 139        KEY_ENTER,
 140        KEY_BRIGHTNESSUP,
 141        KEY_BRIGHTNESSDOWN,
 142        KEY_DOWN,
 143        KEY_UP,
 144        KEY_SCROLLUP,
 145        KEY_SCROLLDOWN,
 146        KEY_LEFTCTRL,
 147        KEY_LEFTALT
 148};
 149
 150static unsigned short keymap_Stylistic_ST5xxx[KEYMAP_LEN] __initdata = {
 151        KEY_RESERVED,
 152        KEY_RESERVED,
 153        KEY_RESERVED,
 154        KEY_RESERVED,
 155        KEY_MAIL,
 156        KEY_ROTATE_DISPLAY,
 157        KEY_ESC,
 158        KEY_ENTER,
 159        KEY_BRIGHTNESSUP,
 160        KEY_BRIGHTNESSDOWN,
 161        KEY_DOWN,
 162        KEY_UP,
 163        KEY_SCROLLUP,
 164        KEY_SCROLLDOWN,
 165        KEY_LEFTCTRL,
 166        KEY_LEFTALT
 167};
 168
 169static struct {
 170        struct input_dev *idev;
 171        struct fujitsu_config config;
 172        unsigned long prev_keymask;
 173
 174        char phys[21];
 175
 176        int irq;
 177        int io_base;
 178        int io_length;
 179} fujitsu;
 180
 181static u8 fujitsu_ack(void)
 182{
 183        return inb(fujitsu.io_base + 2);
 184}
 185
 186static u8 fujitsu_status(void)
 187{
 188        return inb(fujitsu.io_base + 6);
 189}
 190
 191static u8 fujitsu_read_register(const u8 addr)
 192{
 193        outb(addr, fujitsu.io_base);
 194        return inb(fujitsu.io_base + 4);
 195}
 196
 197static void fujitsu_send_state(void)
 198{
 199        int state;
 200        int dock, tablet_mode;
 201
 202        state = fujitsu_read_register(0xdd);
 203
 204        dock = state & 0x02;
 205        if (fujitsu.config.quirks & INVERT_DOCK_STATE_BIT)
 206                dock = !dock;
 207
 208        if ((fujitsu.config.quirks & FORCE_TABLET_MODE_IF_UNDOCK) && (!dock)) {
 209                tablet_mode = 1;
 210        } else{
 211                tablet_mode = state & 0x01;
 212                if (fujitsu.config.quirks & INVERT_TABLET_MODE_BIT)
 213                        tablet_mode = !tablet_mode;
 214        }
 215
 216        input_report_switch(fujitsu.idev, SW_DOCK, dock);
 217        input_report_switch(fujitsu.idev, SW_TABLET_MODE, tablet_mode);
 218        input_sync(fujitsu.idev);
 219}
 220
 221static void fujitsu_reset(void)
 222{
 223        int timeout = 50;
 224
 225        fujitsu_ack();
 226
 227        while ((fujitsu_status() & 0x02) && (--timeout))
 228                msleep(20);
 229
 230        fujitsu_send_state();
 231}
 232
 233static int input_fujitsu_setup(struct device *parent, const char *name,
 234                               const char *phys)
 235{
 236        struct input_dev *idev;
 237        int error;
 238        int i;
 239
 240        idev = input_allocate_device();
 241        if (!idev)
 242                return -ENOMEM;
 243
 244        idev->dev.parent = parent;
 245        idev->phys = phys;
 246        idev->name = name;
 247        idev->id.bustype = BUS_HOST;
 248        idev->id.vendor  = 0x1734;      /* Fujitsu Siemens Computer GmbH */
 249        idev->id.product = 0x0001;
 250        idev->id.version = 0x0101;
 251
 252        idev->keycode = fujitsu.config.keymap;
 253        idev->keycodesize = sizeof(fujitsu.config.keymap[0]);
 254        idev->keycodemax = ARRAY_SIZE(fujitsu.config.keymap);
 255
 256        __set_bit(EV_REP, idev->evbit);
 257
 258        for (i = 0; i < ARRAY_SIZE(fujitsu.config.keymap); i++)
 259                if (fujitsu.config.keymap[i])
 260                        input_set_capability(idev, EV_KEY, fujitsu.config.keymap[i]);
 261
 262        input_set_capability(idev, EV_MSC, MSC_SCAN);
 263
 264        input_set_capability(idev, EV_SW, SW_DOCK);
 265        input_set_capability(idev, EV_SW, SW_TABLET_MODE);
 266
 267        error = input_register_device(idev);
 268        if (error) {
 269                input_free_device(idev);
 270                return error;
 271        }
 272
 273        fujitsu.idev = idev;
 274        return 0;
 275}
 276
 277static void input_fujitsu_remove(void)
 278{
 279        input_unregister_device(fujitsu.idev);
 280}
 281
 282static irqreturn_t fujitsu_interrupt(int irq, void *dev_id)
 283{
 284        unsigned long keymask, changed;
 285        unsigned int keycode;
 286        int pressed;
 287        int i;
 288
 289        if (unlikely(!(fujitsu_status() & 0x01)))
 290                return IRQ_NONE;
 291
 292        fujitsu_send_state();
 293
 294        keymask  = fujitsu_read_register(0xde);
 295        keymask |= fujitsu_read_register(0xdf) << 8;
 296        keymask ^= 0xffff;
 297
 298        changed = keymask ^ fujitsu.prev_keymask;
 299        if (changed) {
 300                fujitsu.prev_keymask = keymask;
 301
 302                for_each_set_bit(i, &changed, KEYMAP_LEN) {
 303                        keycode = fujitsu.config.keymap[i];
 304                        pressed = keymask & changed & BIT(i);
 305
 306                        if (pressed)
 307                                input_event(fujitsu.idev, EV_MSC, MSC_SCAN, i);
 308
 309                        input_report_key(fujitsu.idev, keycode, pressed);
 310                        input_sync(fujitsu.idev);
 311                }
 312        }
 313
 314        fujitsu_ack();
 315        return IRQ_HANDLED;
 316}
 317
 318static void __init fujitsu_dmi_common(const struct dmi_system_id *dmi)
 319{
 320        pr_info("%s\n", dmi->ident);
 321        memcpy(fujitsu.config.keymap, dmi->driver_data,
 322                        sizeof(fujitsu.config.keymap));
 323}
 324
 325static int __init fujitsu_dmi_lifebook(const struct dmi_system_id *dmi)
 326{
 327        fujitsu_dmi_common(dmi);
 328        fujitsu.config.quirks |= INVERT_TABLET_MODE_BIT;
 329        return 1;
 330}
 331
 332static int __init fujitsu_dmi_stylistic(const struct dmi_system_id *dmi)
 333{
 334        fujitsu_dmi_common(dmi);
 335        fujitsu.config.quirks |= FORCE_TABLET_MODE_IF_UNDOCK;
 336        fujitsu.config.quirks |= INVERT_DOCK_STATE_BIT;
 337        return 1;
 338}
 339
 340static const struct dmi_system_id dmi_ids[] __initconst = {
 341        {
 342                .callback = fujitsu_dmi_lifebook,
 343                .ident = "Fujitsu Lifebook T901",
 344                .matches = {
 345                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
 346                        DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook T901")
 347                },
 348                .driver_data = keymap_Lifebook_T901
 349        },
 350        {
 351                .callback = fujitsu_dmi_lifebook,
 352                .ident = "Fujitsu Lifebook T901",
 353                .matches = {
 354                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
 355                        DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T901")
 356                },
 357                .driver_data = keymap_Lifebook_T901
 358        },
 359        {
 360                .callback = fujitsu_dmi_lifebook,
 361                .ident = "Fujitsu Lifebook T902",
 362                .matches = {
 363                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
 364                        DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T902")
 365                },
 366                .driver_data = keymap_Lifebook_T902
 367        },
 368        {
 369                .callback = fujitsu_dmi_lifebook,
 370                .ident = "Fujitsu Siemens P/T Series",
 371                .matches = {
 372                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
 373                        DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK")
 374                },
 375                .driver_data = keymap_Lifebook_Tseries
 376        },
 377        {
 378                .callback = fujitsu_dmi_lifebook,
 379                .ident = "Fujitsu Lifebook T Series",
 380                .matches = {
 381                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
 382                        DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook T")
 383                },
 384                .driver_data = keymap_Lifebook_Tseries
 385        },
 386        {
 387                .callback = fujitsu_dmi_stylistic,
 388                .ident = "Fujitsu Siemens Stylistic T Series",
 389                .matches = {
 390                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
 391                        DMI_MATCH(DMI_PRODUCT_NAME, "Stylistic T")
 392                },
 393                .driver_data = keymap_Stylistic_Tseries
 394        },
 395        {
 396                .callback = fujitsu_dmi_lifebook,
 397                .ident = "Fujitsu LifeBook U810",
 398                .matches = {
 399                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
 400                        DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook U810")
 401                },
 402                .driver_data = keymap_Lifebook_U810
 403        },
 404        {
 405                .callback = fujitsu_dmi_stylistic,
 406                .ident = "Fujitsu Siemens Stylistic ST5xxx Series",
 407                .matches = {
 408                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
 409                        DMI_MATCH(DMI_PRODUCT_NAME, "STYLISTIC ST5")
 410                },
 411                .driver_data = keymap_Stylistic_ST5xxx
 412        },
 413        {
 414                .callback = fujitsu_dmi_stylistic,
 415                .ident = "Fujitsu Siemens Stylistic ST5xxx Series",
 416                .matches = {
 417                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
 418                        DMI_MATCH(DMI_PRODUCT_NAME, "Stylistic ST5")
 419                },
 420                .driver_data = keymap_Stylistic_ST5xxx
 421        },
 422        {
 423                .callback = fujitsu_dmi_lifebook,
 424                .ident = "Unknown (using defaults)",
 425                .matches = {
 426                        DMI_MATCH(DMI_SYS_VENDOR, ""),
 427                        DMI_MATCH(DMI_PRODUCT_NAME, "")
 428                },
 429                .driver_data = keymap_Lifebook_Tseries
 430        },
 431        { NULL }
 432};
 433
 434static acpi_status fujitsu_walk_resources(struct acpi_resource *res, void *data)
 435{
 436        switch (res->type) {
 437        case ACPI_RESOURCE_TYPE_IRQ:
 438                fujitsu.irq = res->data.irq.interrupts[0];
 439                return AE_OK;
 440
 441        case ACPI_RESOURCE_TYPE_IO:
 442                fujitsu.io_base = res->data.io.minimum;
 443                fujitsu.io_length = res->data.io.address_length;
 444                return AE_OK;
 445
 446        case ACPI_RESOURCE_TYPE_END_TAG:
 447                if (fujitsu.irq && fujitsu.io_base)
 448                        return AE_OK;
 449                else
 450                        return AE_NOT_FOUND;
 451
 452        default:
 453                return AE_ERROR;
 454        }
 455}
 456
 457static int acpi_fujitsu_add(struct acpi_device *adev)
 458{
 459        acpi_status status;
 460        int error;
 461
 462        if (!adev)
 463                return -EINVAL;
 464
 465        status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
 466                        fujitsu_walk_resources, NULL);
 467        if (ACPI_FAILURE(status) || !fujitsu.irq || !fujitsu.io_base)
 468                return -ENODEV;
 469
 470        sprintf(acpi_device_name(adev), "Fujitsu %s", acpi_device_hid(adev));
 471        sprintf(acpi_device_class(adev), "%s", ACPI_FUJITSU_CLASS);
 472
 473        snprintf(fujitsu.phys, sizeof(fujitsu.phys),
 474                        "%s/input0", acpi_device_hid(adev));
 475
 476        error = input_fujitsu_setup(&adev->dev,
 477                acpi_device_name(adev), fujitsu.phys);
 478        if (error)
 479                return error;
 480
 481        if (!request_region(fujitsu.io_base, fujitsu.io_length, MODULENAME)) {
 482                input_fujitsu_remove();
 483                return -EBUSY;
 484        }
 485
 486        fujitsu_reset();
 487
 488        error = request_irq(fujitsu.irq, fujitsu_interrupt,
 489                        IRQF_SHARED, MODULENAME, fujitsu_interrupt);
 490        if (error) {
 491                release_region(fujitsu.io_base, fujitsu.io_length);
 492                input_fujitsu_remove();
 493                return error;
 494        }
 495
 496        return 0;
 497}
 498
 499static int acpi_fujitsu_remove(struct acpi_device *adev)
 500{
 501        free_irq(fujitsu.irq, fujitsu_interrupt);
 502        release_region(fujitsu.io_base, fujitsu.io_length);
 503        input_fujitsu_remove();
 504        return 0;
 505}
 506
 507#ifdef CONFIG_PM_SLEEP
 508static int acpi_fujitsu_resume(struct device *dev)
 509{
 510        fujitsu_reset();
 511        return 0;
 512}
 513#endif
 514
 515static SIMPLE_DEV_PM_OPS(acpi_fujitsu_pm, NULL, acpi_fujitsu_resume);
 516
 517static struct acpi_driver acpi_fujitsu_driver = {
 518        .name  = MODULENAME,
 519        .class = "hotkey",
 520        .ids   = fujitsu_ids,
 521        .ops   = {
 522                .add    = acpi_fujitsu_add,
 523                .remove = acpi_fujitsu_remove,
 524        },
 525        .drv.pm = &acpi_fujitsu_pm,
 526};
 527
 528static int __init fujitsu_module_init(void)
 529{
 530        int error;
 531
 532        dmi_check_system(dmi_ids);
 533
 534        error = acpi_bus_register_driver(&acpi_fujitsu_driver);
 535        if (error)
 536                return error;
 537
 538        return 0;
 539}
 540
 541static void __exit fujitsu_module_exit(void)
 542{
 543        acpi_bus_unregister_driver(&acpi_fujitsu_driver);
 544}
 545
 546module_init(fujitsu_module_init);
 547module_exit(fujitsu_module_exit);
 548
 549MODULE_AUTHOR("Robert Gerlach <khnz@gmx.de>");
 550MODULE_DESCRIPTION("Fujitsu tablet pc extras driver");
 551MODULE_LICENSE("GPL");
 552MODULE_VERSION("2.5");
 553
 554MODULE_DEVICE_TABLE(acpi, fujitsu_ids);
 555