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