542 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*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 ": " fmt8 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 0x0127#define INVERT_DOCK_STATE_BIT 0x0228#define FORCE_TABLET_MODE_IF_UNDOCK 0x0429 30#define KEYMAP_LEN 1631 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_LEFTALT60};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_LEFTMETA79};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_LEFTALT117};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_LEFTALT136};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_LEFTALT155};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_T901337 },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_T901346 },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_T902355 },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_Tseries364 },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_Tseries373 },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_Tseries382 },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_U810391 },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_ST5xxx400 },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_ST5xxx409 },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_Tseries418 },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 else438 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 void 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}493 494#ifdef CONFIG_PM_SLEEP495static int acpi_fujitsu_resume(struct device *dev)496{497 fujitsu_reset();498 return 0;499}500#endif501 502static SIMPLE_DEV_PM_OPS(acpi_fujitsu_pm, NULL, acpi_fujitsu_resume);503 504static struct acpi_driver acpi_fujitsu_driver = {505 .name = MODULENAME,506 .class = "hotkey",507 .ids = fujitsu_ids,508 .ops = {509 .add = acpi_fujitsu_add,510 .remove = acpi_fujitsu_remove,511 },512 .drv.pm = &acpi_fujitsu_pm,513};514 515static int __init fujitsu_module_init(void)516{517 int error;518 519 dmi_check_system(dmi_ids);520 521 error = acpi_bus_register_driver(&acpi_fujitsu_driver);522 if (error)523 return error;524 525 return 0;526}527 528static void __exit fujitsu_module_exit(void)529{530 acpi_bus_unregister_driver(&acpi_fujitsu_driver);531}532 533module_init(fujitsu_module_init);534module_exit(fujitsu_module_exit);535 536MODULE_AUTHOR("Robert Gerlach <khnz@gmx.de>");537MODULE_DESCRIPTION("Fujitsu tablet pc extras driver");538MODULE_LICENSE("GPL");539MODULE_VERSION("2.5");540 541MODULE_DEVICE_TABLE(acpi, fujitsu_ids);542