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_DIRECTION,
  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_U810[KEYMAP_LEN] __initdata = {
  75        KEY_RESERVED,
  76        KEY_RESERVED,
  77        KEY_RESERVED,
  78        KEY_RESERVED,
  79        KEY_PROG1,
  80        KEY_PROG2,
  81        KEY_DIRECTION,
  82        KEY_RESERVED,
  83        KEY_RESERVED,
  84        KEY_RESERVED,
  85        KEY_UP,
  86        KEY_DOWN,
  87        KEY_RESERVED,
  88        KEY_RESERVED,
  89        KEY_LEFTCTRL,
  90        KEY_LEFTALT
  91};
  92
  93static unsigned short keymap_Stylistic_Tseries[KEYMAP_LEN] __initdata = {
  94        KEY_RESERVED,
  95        KEY_RESERVED,
  96        KEY_RESERVED,
  97        KEY_RESERVED,
  98        KEY_PRINT,
  99        KEY_BACKSPACE,
 100        KEY_SPACE,
 101        KEY_ENTER,
 102        KEY_BRIGHTNESSUP,
 103        KEY_BRIGHTNESSDOWN,
 104        KEY_DOWN,
 105        KEY_UP,
 106        KEY_SCROLLUP,
 107        KEY_SCROLLDOWN,
 108        KEY_LEFTCTRL,
 109        KEY_LEFTALT
 110};
 111
 112static unsigned short keymap_Stylistic_ST5xxx[KEYMAP_LEN] __initdata = {
 113        KEY_RESERVED,
 114        KEY_RESERVED,
 115        KEY_RESERVED,
 116        KEY_RESERVED,
 117        KEY_MAIL,
 118        KEY_DIRECTION,
 119        KEY_ESC,
 120        KEY_ENTER,
 121        KEY_BRIGHTNESSUP,
 122        KEY_BRIGHTNESSDOWN,
 123        KEY_DOWN,
 124        KEY_UP,
 125        KEY_SCROLLUP,
 126        KEY_SCROLLDOWN,
 127        KEY_LEFTCTRL,
 128        KEY_LEFTALT
 129};
 130
 131static struct {
 132        struct input_dev *idev;
 133        struct fujitsu_config config;
 134        unsigned long prev_keymask;
 135
 136        char phys[21];
 137
 138        int irq;
 139        int io_base;
 140        int io_length;
 141} fujitsu;
 142
 143static u8 fujitsu_ack(void)
 144{
 145        return inb(fujitsu.io_base + 2);
 146}
 147
 148static u8 fujitsu_status(void)
 149{
 150        return inb(fujitsu.io_base + 6);
 151}
 152
 153static u8 fujitsu_read_register(const u8 addr)
 154{
 155        outb(addr, fujitsu.io_base);
 156        return inb(fujitsu.io_base + 4);
 157}
 158
 159static void fujitsu_send_state(void)
 160{
 161        int state;
 162        int dock, tablet_mode;
 163
 164        state = fujitsu_read_register(0xdd);
 165
 166        dock = state & 0x02;
 167        if (fujitsu.config.quirks & INVERT_DOCK_STATE_BIT)
 168                dock = !dock;
 169
 170        if ((fujitsu.config.quirks & FORCE_TABLET_MODE_IF_UNDOCK) && (!dock)) {
 171                tablet_mode = 1;
 172        } else{
 173                tablet_mode = state & 0x01;
 174                if (fujitsu.config.quirks & INVERT_TABLET_MODE_BIT)
 175                        tablet_mode = !tablet_mode;
 176        }
 177
 178        input_report_switch(fujitsu.idev, SW_DOCK, dock);
 179        input_report_switch(fujitsu.idev, SW_TABLET_MODE, tablet_mode);
 180        input_sync(fujitsu.idev);
 181}
 182
 183static void fujitsu_reset(void)
 184{
 185        int timeout = 50;
 186
 187        fujitsu_ack();
 188
 189        while ((fujitsu_status() & 0x02) && (--timeout))
 190                msleep(20);
 191
 192        fujitsu_send_state();
 193}
 194
 195static int input_fujitsu_setup(struct device *parent, const char *name,
 196                               const char *phys)
 197{
 198        struct input_dev *idev;
 199        int error;
 200        int i;
 201
 202        idev = input_allocate_device();
 203        if (!idev)
 204                return -ENOMEM;
 205
 206        idev->dev.parent = parent;
 207        idev->phys = phys;
 208        idev->name = name;
 209        idev->id.bustype = BUS_HOST;
 210        idev->id.vendor  = 0x1734;      /* Fujitsu Siemens Computer GmbH */
 211        idev->id.product = 0x0001;
 212        idev->id.version = 0x0101;
 213
 214        idev->keycode = fujitsu.config.keymap;
 215        idev->keycodesize = sizeof(fujitsu.config.keymap[0]);
 216        idev->keycodemax = ARRAY_SIZE(fujitsu.config.keymap);
 217
 218        __set_bit(EV_REP, idev->evbit);
 219
 220        for (i = 0; i < ARRAY_SIZE(fujitsu.config.keymap); i++)
 221                if (fujitsu.config.keymap[i])
 222                        input_set_capability(idev, EV_KEY, fujitsu.config.keymap[i]);
 223
 224        input_set_capability(idev, EV_MSC, MSC_SCAN);
 225
 226        input_set_capability(idev, EV_SW, SW_DOCK);
 227        input_set_capability(idev, EV_SW, SW_TABLET_MODE);
 228
 229        error = input_register_device(idev);
 230        if (error) {
 231                input_free_device(idev);
 232                return error;
 233        }
 234
 235        fujitsu.idev = idev;
 236        return 0;
 237}
 238
 239static void input_fujitsu_remove(void)
 240{
 241        input_unregister_device(fujitsu.idev);
 242}
 243
 244static irqreturn_t fujitsu_interrupt(int irq, void *dev_id)
 245{
 246        unsigned long keymask, changed;
 247        unsigned int keycode;
 248        int pressed;
 249        int i;
 250
 251        if (unlikely(!(fujitsu_status() & 0x01)))
 252                return IRQ_NONE;
 253
 254        fujitsu_send_state();
 255
 256        keymask  = fujitsu_read_register(0xde);
 257        keymask |= fujitsu_read_register(0xdf) << 8;
 258        keymask ^= 0xffff;
 259
 260        changed = keymask ^ fujitsu.prev_keymask;
 261        if (changed) {
 262                fujitsu.prev_keymask = keymask;
 263
 264                for_each_set_bit(i, &changed, KEYMAP_LEN) {
 265                        keycode = fujitsu.config.keymap[i];
 266                        pressed = keymask & changed & BIT(i);
 267
 268                        if (pressed)
 269                                input_event(fujitsu.idev, EV_MSC, MSC_SCAN, i);
 270
 271                        input_report_key(fujitsu.idev, keycode, pressed);
 272                        input_sync(fujitsu.idev);
 273                }
 274        }
 275
 276        fujitsu_ack();
 277        return IRQ_HANDLED;
 278}
 279
 280static void fujitsu_dmi_common(const struct dmi_system_id *dmi)
 281{
 282        pr_info("%s\n", dmi->ident);
 283        memcpy(fujitsu.config.keymap, dmi->driver_data,
 284                        sizeof(fujitsu.config.keymap));
 285}
 286
 287static int fujitsu_dmi_lifebook(const struct dmi_system_id *dmi)
 288{
 289        fujitsu_dmi_common(dmi);
 290        fujitsu.config.quirks |= INVERT_TABLET_MODE_BIT;
 291        return 1;
 292}
 293
 294static int fujitsu_dmi_stylistic(const struct dmi_system_id *dmi)
 295{
 296        fujitsu_dmi_common(dmi);
 297        fujitsu.config.quirks |= FORCE_TABLET_MODE_IF_UNDOCK;
 298        fujitsu.config.quirks |= INVERT_DOCK_STATE_BIT;
 299        return 1;
 300}
 301
 302static const struct dmi_system_id dmi_ids[] __initconst = {
 303        {
 304                .callback = fujitsu_dmi_lifebook,
 305                .ident = "Fujitsu Siemens P/T Series",
 306                .matches = {
 307                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
 308                        DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK")
 309                },
 310                .driver_data = keymap_Lifebook_Tseries
 311        },
 312        {
 313                .callback = fujitsu_dmi_lifebook,
 314                .ident = "Fujitsu Lifebook T Series",
 315                .matches = {
 316                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
 317                        DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook T")
 318                },
 319                .driver_data = keymap_Lifebook_Tseries
 320        },
 321        {
 322                .callback = fujitsu_dmi_stylistic,
 323                .ident = "Fujitsu Siemens Stylistic T Series",
 324                .matches = {
 325                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
 326                        DMI_MATCH(DMI_PRODUCT_NAME, "Stylistic T")
 327                },
 328                .driver_data = keymap_Stylistic_Tseries
 329        },
 330        {
 331                .callback = fujitsu_dmi_lifebook,
 332                .ident = "Fujitsu LifeBook U810",
 333                .matches = {
 334                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
 335                        DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook U810")
 336                },
 337                .driver_data = keymap_Lifebook_U810
 338        },
 339        {
 340                .callback = fujitsu_dmi_stylistic,
 341                .ident = "Fujitsu Siemens Stylistic ST5xxx Series",
 342                .matches = {
 343                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
 344                        DMI_MATCH(DMI_PRODUCT_NAME, "STYLISTIC ST5")
 345                },
 346                .driver_data = keymap_Stylistic_ST5xxx
 347        },
 348        {
 349                .callback = fujitsu_dmi_stylistic,
 350                .ident = "Fujitsu Siemens Stylistic ST5xxx Series",
 351                .matches = {
 352                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
 353                        DMI_MATCH(DMI_PRODUCT_NAME, "Stylistic ST5")
 354                },
 355                .driver_data = keymap_Stylistic_ST5xxx
 356        },
 357        {
 358                .callback = fujitsu_dmi_lifebook,
 359                .ident = "Unknown (using defaults)",
 360                .matches = {
 361                        DMI_MATCH(DMI_SYS_VENDOR, ""),
 362                        DMI_MATCH(DMI_PRODUCT_NAME, "")
 363                },
 364                .driver_data = keymap_Lifebook_Tseries
 365        },
 366        { NULL }
 367};
 368
 369static acpi_status fujitsu_walk_resources(struct acpi_resource *res, void *data)
 370{
 371        switch (res->type) {
 372        case ACPI_RESOURCE_TYPE_IRQ:
 373                fujitsu.irq = res->data.irq.interrupts[0];
 374                return AE_OK;
 375
 376        case ACPI_RESOURCE_TYPE_IO:
 377                fujitsu.io_base = res->data.io.minimum;
 378                fujitsu.io_length = res->data.io.address_length;
 379                return AE_OK;
 380
 381        case ACPI_RESOURCE_TYPE_END_TAG:
 382                if (fujitsu.irq && fujitsu.io_base)
 383                        return AE_OK;
 384                else
 385                        return AE_NOT_FOUND;
 386
 387        default:
 388                return AE_ERROR;
 389        }
 390}
 391
 392static int acpi_fujitsu_add(struct acpi_device *adev)
 393{
 394        acpi_status status;
 395        int error;
 396
 397        if (!adev)
 398                return -EINVAL;
 399
 400        status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
 401                        fujitsu_walk_resources, NULL);
 402        if (ACPI_FAILURE(status) || !fujitsu.irq || !fujitsu.io_base)
 403                return -ENODEV;
 404
 405        sprintf(acpi_device_name(adev), "Fujitsu %s", acpi_device_hid(adev));
 406        sprintf(acpi_device_class(adev), "%s", ACPI_FUJITSU_CLASS);
 407
 408        snprintf(fujitsu.phys, sizeof(fujitsu.phys),
 409                        "%s/input0", acpi_device_hid(adev));
 410
 411        error = input_fujitsu_setup(&adev->dev,
 412                acpi_device_name(adev), fujitsu.phys);
 413        if (error)
 414                return error;
 415
 416        if (!request_region(fujitsu.io_base, fujitsu.io_length, MODULENAME)) {
 417                input_fujitsu_remove();
 418                return -EBUSY;
 419        }
 420
 421        fujitsu_reset();
 422
 423        error = request_irq(fujitsu.irq, fujitsu_interrupt,
 424                        IRQF_SHARED, MODULENAME, fujitsu_interrupt);
 425        if (error) {
 426                release_region(fujitsu.io_base, fujitsu.io_length);
 427                input_fujitsu_remove();
 428                return error;
 429        }
 430
 431        return 0;
 432}
 433
 434static int acpi_fujitsu_remove(struct acpi_device *adev)
 435{
 436        free_irq(fujitsu.irq, fujitsu_interrupt);
 437        release_region(fujitsu.io_base, fujitsu.io_length);
 438        input_fujitsu_remove();
 439        return 0;
 440}
 441
 442#ifdef CONFIG_PM_SLEEP
 443static int acpi_fujitsu_resume(struct device *dev)
 444{
 445        fujitsu_reset();
 446        return 0;
 447}
 448#endif
 449
 450static SIMPLE_DEV_PM_OPS(acpi_fujitsu_pm, NULL, acpi_fujitsu_resume);
 451
 452static struct acpi_driver acpi_fujitsu_driver = {
 453        .name  = MODULENAME,
 454        .class = "hotkey",
 455        .ids   = fujitsu_ids,
 456        .ops   = {
 457                .add    = acpi_fujitsu_add,
 458                .remove = acpi_fujitsu_remove,
 459        },
 460        .drv.pm = &acpi_fujitsu_pm,
 461};
 462
 463static int __init fujitsu_module_init(void)
 464{
 465        int error;
 466
 467        dmi_check_system(dmi_ids);
 468
 469        error = acpi_bus_register_driver(&acpi_fujitsu_driver);
 470        if (error)
 471                return error;
 472
 473        return 0;
 474}
 475
 476static void __exit fujitsu_module_exit(void)
 477{
 478        acpi_bus_unregister_driver(&acpi_fujitsu_driver);
 479}
 480
 481module_init(fujitsu_module_init);
 482module_exit(fujitsu_module_exit);
 483
 484MODULE_AUTHOR("Robert Gerlach <khnz@gmx.de>");
 485MODULE_DESCRIPTION("Fujitsu tablet pc extras driver");
 486MODULE_LICENSE("GPL");
 487MODULE_VERSION("2.5");
 488
 489MODULE_DEVICE_TABLE(acpi, fujitsu_ids);
 490
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.