162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2006-2012 Robert Gerlach <khnz@gmx.de>
462306a36Sopenharmony_ci * Copyright (C) 2005-2006 Jan Rychter <jan@rychter.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/bitops.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci#include <linux/ioport.h>
1562306a36Sopenharmony_ci#include <linux/acpi.h>
1662306a36Sopenharmony_ci#include <linux/device.h>
1762306a36Sopenharmony_ci#include <linux/interrupt.h>
1862306a36Sopenharmony_ci#include <linux/input.h>
1962306a36Sopenharmony_ci#include <linux/delay.h>
2062306a36Sopenharmony_ci#include <linux/dmi.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define MODULENAME "fujitsu-tablet"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define ACPI_FUJITSU_CLASS "fujitsu"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define INVERT_TABLET_MODE_BIT      0x01
2762306a36Sopenharmony_ci#define INVERT_DOCK_STATE_BIT       0x02
2862306a36Sopenharmony_ci#define FORCE_TABLET_MODE_IF_UNDOCK 0x04
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define KEYMAP_LEN 16
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic const struct acpi_device_id fujitsu_ids[] = {
3362306a36Sopenharmony_ci	{ .id = "FUJ02BD" },
3462306a36Sopenharmony_ci	{ .id = "FUJ02BF" },
3562306a36Sopenharmony_ci	{ .id = "" }
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistruct fujitsu_config {
3962306a36Sopenharmony_ci	unsigned short keymap[KEYMAP_LEN];
4062306a36Sopenharmony_ci	unsigned int quirks;
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initdata = {
4462306a36Sopenharmony_ci	KEY_RESERVED,
4562306a36Sopenharmony_ci	KEY_RESERVED,
4662306a36Sopenharmony_ci	KEY_RESERVED,
4762306a36Sopenharmony_ci	KEY_RESERVED,
4862306a36Sopenharmony_ci	KEY_SCROLLDOWN,
4962306a36Sopenharmony_ci	KEY_SCROLLUP,
5062306a36Sopenharmony_ci	KEY_ROTATE_DISPLAY,
5162306a36Sopenharmony_ci	KEY_LEFTCTRL,
5262306a36Sopenharmony_ci	KEY_BRIGHTNESSUP,
5362306a36Sopenharmony_ci	KEY_BRIGHTNESSDOWN,
5462306a36Sopenharmony_ci	KEY_BRIGHTNESS_ZERO,
5562306a36Sopenharmony_ci	KEY_RESERVED,
5662306a36Sopenharmony_ci	KEY_RESERVED,
5762306a36Sopenharmony_ci	KEY_RESERVED,
5862306a36Sopenharmony_ci	KEY_RESERVED,
5962306a36Sopenharmony_ci	KEY_LEFTALT
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic unsigned short keymap_Lifebook_T901[KEYMAP_LEN] __initdata = {
6362306a36Sopenharmony_ci	KEY_RESERVED,
6462306a36Sopenharmony_ci	KEY_RESERVED,
6562306a36Sopenharmony_ci	KEY_RESERVED,
6662306a36Sopenharmony_ci	KEY_RESERVED,
6762306a36Sopenharmony_ci	KEY_SCROLLDOWN,
6862306a36Sopenharmony_ci	KEY_SCROLLUP,
6962306a36Sopenharmony_ci	KEY_CYCLEWINDOWS,
7062306a36Sopenharmony_ci	KEY_LEFTCTRL,
7162306a36Sopenharmony_ci	KEY_RESERVED,
7262306a36Sopenharmony_ci	KEY_RESERVED,
7362306a36Sopenharmony_ci	KEY_RESERVED,
7462306a36Sopenharmony_ci	KEY_RESERVED,
7562306a36Sopenharmony_ci	KEY_RESERVED,
7662306a36Sopenharmony_ci	KEY_RESERVED,
7762306a36Sopenharmony_ci	KEY_RESERVED,
7862306a36Sopenharmony_ci	KEY_LEFTMETA
7962306a36Sopenharmony_ci};
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic unsigned short keymap_Lifebook_T902[KEYMAP_LEN] __initdata = {
8262306a36Sopenharmony_ci	KEY_RESERVED,
8362306a36Sopenharmony_ci	KEY_VOLUMEDOWN,
8462306a36Sopenharmony_ci	KEY_VOLUMEUP,
8562306a36Sopenharmony_ci	KEY_CYCLEWINDOWS,
8662306a36Sopenharmony_ci	KEY_PROG1,
8762306a36Sopenharmony_ci	KEY_PROG2,
8862306a36Sopenharmony_ci	KEY_LEFTMETA,
8962306a36Sopenharmony_ci	KEY_RESERVED,
9062306a36Sopenharmony_ci	KEY_RESERVED,
9162306a36Sopenharmony_ci	KEY_RESERVED,
9262306a36Sopenharmony_ci	KEY_RESERVED,
9362306a36Sopenharmony_ci	KEY_RESERVED,
9462306a36Sopenharmony_ci	KEY_RESERVED,
9562306a36Sopenharmony_ci	KEY_RESERVED,
9662306a36Sopenharmony_ci	KEY_RESERVED,
9762306a36Sopenharmony_ci	KEY_RESERVED,
9862306a36Sopenharmony_ci};
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initdata = {
10162306a36Sopenharmony_ci	KEY_RESERVED,
10262306a36Sopenharmony_ci	KEY_RESERVED,
10362306a36Sopenharmony_ci	KEY_RESERVED,
10462306a36Sopenharmony_ci	KEY_RESERVED,
10562306a36Sopenharmony_ci	KEY_PROG1,
10662306a36Sopenharmony_ci	KEY_PROG2,
10762306a36Sopenharmony_ci	KEY_ROTATE_DISPLAY,
10862306a36Sopenharmony_ci	KEY_RESERVED,
10962306a36Sopenharmony_ci	KEY_RESERVED,
11062306a36Sopenharmony_ci	KEY_RESERVED,
11162306a36Sopenharmony_ci	KEY_UP,
11262306a36Sopenharmony_ci	KEY_DOWN,
11362306a36Sopenharmony_ci	KEY_RESERVED,
11462306a36Sopenharmony_ci	KEY_RESERVED,
11562306a36Sopenharmony_ci	KEY_LEFTCTRL,
11662306a36Sopenharmony_ci	KEY_LEFTALT
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic unsigned short keymap_Stylistic_Tseries[KEYMAP_LEN] __initdata = {
12062306a36Sopenharmony_ci	KEY_RESERVED,
12162306a36Sopenharmony_ci	KEY_RESERVED,
12262306a36Sopenharmony_ci	KEY_RESERVED,
12362306a36Sopenharmony_ci	KEY_RESERVED,
12462306a36Sopenharmony_ci	KEY_PRINT,
12562306a36Sopenharmony_ci	KEY_BACKSPACE,
12662306a36Sopenharmony_ci	KEY_SPACE,
12762306a36Sopenharmony_ci	KEY_ENTER,
12862306a36Sopenharmony_ci	KEY_BRIGHTNESSUP,
12962306a36Sopenharmony_ci	KEY_BRIGHTNESSDOWN,
13062306a36Sopenharmony_ci	KEY_DOWN,
13162306a36Sopenharmony_ci	KEY_UP,
13262306a36Sopenharmony_ci	KEY_SCROLLUP,
13362306a36Sopenharmony_ci	KEY_SCROLLDOWN,
13462306a36Sopenharmony_ci	KEY_LEFTCTRL,
13562306a36Sopenharmony_ci	KEY_LEFTALT
13662306a36Sopenharmony_ci};
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic unsigned short keymap_Stylistic_ST5xxx[KEYMAP_LEN] __initdata = {
13962306a36Sopenharmony_ci	KEY_RESERVED,
14062306a36Sopenharmony_ci	KEY_RESERVED,
14162306a36Sopenharmony_ci	KEY_RESERVED,
14262306a36Sopenharmony_ci	KEY_RESERVED,
14362306a36Sopenharmony_ci	KEY_MAIL,
14462306a36Sopenharmony_ci	KEY_ROTATE_DISPLAY,
14562306a36Sopenharmony_ci	KEY_ESC,
14662306a36Sopenharmony_ci	KEY_ENTER,
14762306a36Sopenharmony_ci	KEY_BRIGHTNESSUP,
14862306a36Sopenharmony_ci	KEY_BRIGHTNESSDOWN,
14962306a36Sopenharmony_ci	KEY_DOWN,
15062306a36Sopenharmony_ci	KEY_UP,
15162306a36Sopenharmony_ci	KEY_SCROLLUP,
15262306a36Sopenharmony_ci	KEY_SCROLLDOWN,
15362306a36Sopenharmony_ci	KEY_LEFTCTRL,
15462306a36Sopenharmony_ci	KEY_LEFTALT
15562306a36Sopenharmony_ci};
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic struct {
15862306a36Sopenharmony_ci	struct input_dev *idev;
15962306a36Sopenharmony_ci	struct fujitsu_config config;
16062306a36Sopenharmony_ci	unsigned long prev_keymask;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	char phys[21];
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	int irq;
16562306a36Sopenharmony_ci	int io_base;
16662306a36Sopenharmony_ci	int io_length;
16762306a36Sopenharmony_ci} fujitsu;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic u8 fujitsu_ack(void)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	return inb(fujitsu.io_base + 2);
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic u8 fujitsu_status(void)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	return inb(fujitsu.io_base + 6);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic u8 fujitsu_read_register(const u8 addr)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	outb(addr, fujitsu.io_base);
18262306a36Sopenharmony_ci	return inb(fujitsu.io_base + 4);
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic void fujitsu_send_state(void)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	int state;
18862306a36Sopenharmony_ci	int dock, tablet_mode;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	state = fujitsu_read_register(0xdd);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	dock = state & 0x02;
19362306a36Sopenharmony_ci	if (fujitsu.config.quirks & INVERT_DOCK_STATE_BIT)
19462306a36Sopenharmony_ci		dock = !dock;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if ((fujitsu.config.quirks & FORCE_TABLET_MODE_IF_UNDOCK) && (!dock)) {
19762306a36Sopenharmony_ci		tablet_mode = 1;
19862306a36Sopenharmony_ci	} else{
19962306a36Sopenharmony_ci		tablet_mode = state & 0x01;
20062306a36Sopenharmony_ci		if (fujitsu.config.quirks & INVERT_TABLET_MODE_BIT)
20162306a36Sopenharmony_ci			tablet_mode = !tablet_mode;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	input_report_switch(fujitsu.idev, SW_DOCK, dock);
20562306a36Sopenharmony_ci	input_report_switch(fujitsu.idev, SW_TABLET_MODE, tablet_mode);
20662306a36Sopenharmony_ci	input_sync(fujitsu.idev);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic void fujitsu_reset(void)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	int timeout = 50;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	fujitsu_ack();
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	while ((fujitsu_status() & 0x02) && (--timeout))
21662306a36Sopenharmony_ci		msleep(20);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	fujitsu_send_state();
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic int input_fujitsu_setup(struct device *parent, const char *name,
22262306a36Sopenharmony_ci			       const char *phys)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct input_dev *idev;
22562306a36Sopenharmony_ci	int error;
22662306a36Sopenharmony_ci	int i;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	idev = input_allocate_device();
22962306a36Sopenharmony_ci	if (!idev)
23062306a36Sopenharmony_ci		return -ENOMEM;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	idev->dev.parent = parent;
23362306a36Sopenharmony_ci	idev->phys = phys;
23462306a36Sopenharmony_ci	idev->name = name;
23562306a36Sopenharmony_ci	idev->id.bustype = BUS_HOST;
23662306a36Sopenharmony_ci	idev->id.vendor  = 0x1734;	/* Fujitsu Siemens Computer GmbH */
23762306a36Sopenharmony_ci	idev->id.product = 0x0001;
23862306a36Sopenharmony_ci	idev->id.version = 0x0101;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	idev->keycode = fujitsu.config.keymap;
24162306a36Sopenharmony_ci	idev->keycodesize = sizeof(fujitsu.config.keymap[0]);
24262306a36Sopenharmony_ci	idev->keycodemax = ARRAY_SIZE(fujitsu.config.keymap);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	__set_bit(EV_REP, idev->evbit);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(fujitsu.config.keymap); i++)
24762306a36Sopenharmony_ci		if (fujitsu.config.keymap[i])
24862306a36Sopenharmony_ci			input_set_capability(idev, EV_KEY, fujitsu.config.keymap[i]);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	input_set_capability(idev, EV_MSC, MSC_SCAN);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	input_set_capability(idev, EV_SW, SW_DOCK);
25362306a36Sopenharmony_ci	input_set_capability(idev, EV_SW, SW_TABLET_MODE);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	error = input_register_device(idev);
25662306a36Sopenharmony_ci	if (error) {
25762306a36Sopenharmony_ci		input_free_device(idev);
25862306a36Sopenharmony_ci		return error;
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	fujitsu.idev = idev;
26262306a36Sopenharmony_ci	return 0;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic void input_fujitsu_remove(void)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	input_unregister_device(fujitsu.idev);
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic irqreturn_t fujitsu_interrupt(int irq, void *dev_id)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	unsigned long keymask, changed;
27362306a36Sopenharmony_ci	unsigned int keycode;
27462306a36Sopenharmony_ci	int pressed;
27562306a36Sopenharmony_ci	int i;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if (unlikely(!(fujitsu_status() & 0x01)))
27862306a36Sopenharmony_ci		return IRQ_NONE;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	fujitsu_send_state();
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	keymask  = fujitsu_read_register(0xde);
28362306a36Sopenharmony_ci	keymask |= fujitsu_read_register(0xdf) << 8;
28462306a36Sopenharmony_ci	keymask ^= 0xffff;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	changed = keymask ^ fujitsu.prev_keymask;
28762306a36Sopenharmony_ci	if (changed) {
28862306a36Sopenharmony_ci		fujitsu.prev_keymask = keymask;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		for_each_set_bit(i, &changed, KEYMAP_LEN) {
29162306a36Sopenharmony_ci			keycode = fujitsu.config.keymap[i];
29262306a36Sopenharmony_ci			pressed = keymask & changed & BIT(i);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci			if (pressed)
29562306a36Sopenharmony_ci				input_event(fujitsu.idev, EV_MSC, MSC_SCAN, i);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci			input_report_key(fujitsu.idev, keycode, pressed);
29862306a36Sopenharmony_ci			input_sync(fujitsu.idev);
29962306a36Sopenharmony_ci		}
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	fujitsu_ack();
30362306a36Sopenharmony_ci	return IRQ_HANDLED;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic void __init fujitsu_dmi_common(const struct dmi_system_id *dmi)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	pr_info("%s\n", dmi->ident);
30962306a36Sopenharmony_ci	memcpy(fujitsu.config.keymap, dmi->driver_data,
31062306a36Sopenharmony_ci			sizeof(fujitsu.config.keymap));
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic int __init fujitsu_dmi_lifebook(const struct dmi_system_id *dmi)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	fujitsu_dmi_common(dmi);
31662306a36Sopenharmony_ci	fujitsu.config.quirks |= INVERT_TABLET_MODE_BIT;
31762306a36Sopenharmony_ci	return 1;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic int __init fujitsu_dmi_stylistic(const struct dmi_system_id *dmi)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	fujitsu_dmi_common(dmi);
32362306a36Sopenharmony_ci	fujitsu.config.quirks |= FORCE_TABLET_MODE_IF_UNDOCK;
32462306a36Sopenharmony_ci	fujitsu.config.quirks |= INVERT_DOCK_STATE_BIT;
32562306a36Sopenharmony_ci	return 1;
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic const struct dmi_system_id dmi_ids[] __initconst = {
32962306a36Sopenharmony_ci	{
33062306a36Sopenharmony_ci		.callback = fujitsu_dmi_lifebook,
33162306a36Sopenharmony_ci		.ident = "Fujitsu Lifebook T901",
33262306a36Sopenharmony_ci		.matches = {
33362306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
33462306a36Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook T901")
33562306a36Sopenharmony_ci		},
33662306a36Sopenharmony_ci		.driver_data = keymap_Lifebook_T901
33762306a36Sopenharmony_ci	},
33862306a36Sopenharmony_ci	{
33962306a36Sopenharmony_ci		.callback = fujitsu_dmi_lifebook,
34062306a36Sopenharmony_ci		.ident = "Fujitsu Lifebook T901",
34162306a36Sopenharmony_ci		.matches = {
34262306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
34362306a36Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T901")
34462306a36Sopenharmony_ci		},
34562306a36Sopenharmony_ci		.driver_data = keymap_Lifebook_T901
34662306a36Sopenharmony_ci	},
34762306a36Sopenharmony_ci	{
34862306a36Sopenharmony_ci		.callback = fujitsu_dmi_lifebook,
34962306a36Sopenharmony_ci		.ident = "Fujitsu Lifebook T902",
35062306a36Sopenharmony_ci		.matches = {
35162306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
35262306a36Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T902")
35362306a36Sopenharmony_ci		},
35462306a36Sopenharmony_ci		.driver_data = keymap_Lifebook_T902
35562306a36Sopenharmony_ci	},
35662306a36Sopenharmony_ci	{
35762306a36Sopenharmony_ci		.callback = fujitsu_dmi_lifebook,
35862306a36Sopenharmony_ci		.ident = "Fujitsu Siemens P/T Series",
35962306a36Sopenharmony_ci		.matches = {
36062306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
36162306a36Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK")
36262306a36Sopenharmony_ci		},
36362306a36Sopenharmony_ci		.driver_data = keymap_Lifebook_Tseries
36462306a36Sopenharmony_ci	},
36562306a36Sopenharmony_ci	{
36662306a36Sopenharmony_ci		.callback = fujitsu_dmi_lifebook,
36762306a36Sopenharmony_ci		.ident = "Fujitsu Lifebook T Series",
36862306a36Sopenharmony_ci		.matches = {
36962306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
37062306a36Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook T")
37162306a36Sopenharmony_ci		},
37262306a36Sopenharmony_ci		.driver_data = keymap_Lifebook_Tseries
37362306a36Sopenharmony_ci	},
37462306a36Sopenharmony_ci	{
37562306a36Sopenharmony_ci		.callback = fujitsu_dmi_stylistic,
37662306a36Sopenharmony_ci		.ident = "Fujitsu Siemens Stylistic T Series",
37762306a36Sopenharmony_ci		.matches = {
37862306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
37962306a36Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_NAME, "Stylistic T")
38062306a36Sopenharmony_ci		},
38162306a36Sopenharmony_ci		.driver_data = keymap_Stylistic_Tseries
38262306a36Sopenharmony_ci	},
38362306a36Sopenharmony_ci	{
38462306a36Sopenharmony_ci		.callback = fujitsu_dmi_lifebook,
38562306a36Sopenharmony_ci		.ident = "Fujitsu LifeBook U810",
38662306a36Sopenharmony_ci		.matches = {
38762306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
38862306a36Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook U810")
38962306a36Sopenharmony_ci		},
39062306a36Sopenharmony_ci		.driver_data = keymap_Lifebook_U810
39162306a36Sopenharmony_ci	},
39262306a36Sopenharmony_ci	{
39362306a36Sopenharmony_ci		.callback = fujitsu_dmi_stylistic,
39462306a36Sopenharmony_ci		.ident = "Fujitsu Siemens Stylistic ST5xxx Series",
39562306a36Sopenharmony_ci		.matches = {
39662306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
39762306a36Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_NAME, "STYLISTIC ST5")
39862306a36Sopenharmony_ci		},
39962306a36Sopenharmony_ci		.driver_data = keymap_Stylistic_ST5xxx
40062306a36Sopenharmony_ci	},
40162306a36Sopenharmony_ci	{
40262306a36Sopenharmony_ci		.callback = fujitsu_dmi_stylistic,
40362306a36Sopenharmony_ci		.ident = "Fujitsu Siemens Stylistic ST5xxx Series",
40462306a36Sopenharmony_ci		.matches = {
40562306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
40662306a36Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_NAME, "Stylistic ST5")
40762306a36Sopenharmony_ci		},
40862306a36Sopenharmony_ci		.driver_data = keymap_Stylistic_ST5xxx
40962306a36Sopenharmony_ci	},
41062306a36Sopenharmony_ci	{
41162306a36Sopenharmony_ci		.callback = fujitsu_dmi_lifebook,
41262306a36Sopenharmony_ci		.ident = "Unknown (using defaults)",
41362306a36Sopenharmony_ci		.matches = {
41462306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, ""),
41562306a36Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_NAME, "")
41662306a36Sopenharmony_ci		},
41762306a36Sopenharmony_ci		.driver_data = keymap_Lifebook_Tseries
41862306a36Sopenharmony_ci	},
41962306a36Sopenharmony_ci	{ NULL }
42062306a36Sopenharmony_ci};
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic acpi_status fujitsu_walk_resources(struct acpi_resource *res, void *data)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	switch (res->type) {
42562306a36Sopenharmony_ci	case ACPI_RESOURCE_TYPE_IRQ:
42662306a36Sopenharmony_ci		fujitsu.irq = res->data.irq.interrupts[0];
42762306a36Sopenharmony_ci		return AE_OK;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	case ACPI_RESOURCE_TYPE_IO:
43062306a36Sopenharmony_ci		fujitsu.io_base = res->data.io.minimum;
43162306a36Sopenharmony_ci		fujitsu.io_length = res->data.io.address_length;
43262306a36Sopenharmony_ci		return AE_OK;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	case ACPI_RESOURCE_TYPE_END_TAG:
43562306a36Sopenharmony_ci		if (fujitsu.irq && fujitsu.io_base)
43662306a36Sopenharmony_ci			return AE_OK;
43762306a36Sopenharmony_ci		else
43862306a36Sopenharmony_ci			return AE_NOT_FOUND;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	default:
44162306a36Sopenharmony_ci		return AE_ERROR;
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_cistatic int acpi_fujitsu_add(struct acpi_device *adev)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	acpi_status status;
44862306a36Sopenharmony_ci	int error;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (!adev)
45162306a36Sopenharmony_ci		return -EINVAL;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
45462306a36Sopenharmony_ci			fujitsu_walk_resources, NULL);
45562306a36Sopenharmony_ci	if (ACPI_FAILURE(status) || !fujitsu.irq || !fujitsu.io_base)
45662306a36Sopenharmony_ci		return -ENODEV;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	sprintf(acpi_device_name(adev), "Fujitsu %s", acpi_device_hid(adev));
45962306a36Sopenharmony_ci	sprintf(acpi_device_class(adev), "%s", ACPI_FUJITSU_CLASS);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	snprintf(fujitsu.phys, sizeof(fujitsu.phys),
46262306a36Sopenharmony_ci			"%s/input0", acpi_device_hid(adev));
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	error = input_fujitsu_setup(&adev->dev,
46562306a36Sopenharmony_ci		acpi_device_name(adev), fujitsu.phys);
46662306a36Sopenharmony_ci	if (error)
46762306a36Sopenharmony_ci		return error;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	if (!request_region(fujitsu.io_base, fujitsu.io_length, MODULENAME)) {
47062306a36Sopenharmony_ci		input_fujitsu_remove();
47162306a36Sopenharmony_ci		return -EBUSY;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	fujitsu_reset();
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	error = request_irq(fujitsu.irq, fujitsu_interrupt,
47762306a36Sopenharmony_ci			IRQF_SHARED, MODULENAME, fujitsu_interrupt);
47862306a36Sopenharmony_ci	if (error) {
47962306a36Sopenharmony_ci		release_region(fujitsu.io_base, fujitsu.io_length);
48062306a36Sopenharmony_ci		input_fujitsu_remove();
48162306a36Sopenharmony_ci		return error;
48262306a36Sopenharmony_ci	}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	return 0;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic void acpi_fujitsu_remove(struct acpi_device *adev)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	free_irq(fujitsu.irq, fujitsu_interrupt);
49062306a36Sopenharmony_ci	release_region(fujitsu.io_base, fujitsu.io_length);
49162306a36Sopenharmony_ci	input_fujitsu_remove();
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
49562306a36Sopenharmony_cistatic int acpi_fujitsu_resume(struct device *dev)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	fujitsu_reset();
49862306a36Sopenharmony_ci	return 0;
49962306a36Sopenharmony_ci}
50062306a36Sopenharmony_ci#endif
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(acpi_fujitsu_pm, NULL, acpi_fujitsu_resume);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic struct acpi_driver acpi_fujitsu_driver = {
50562306a36Sopenharmony_ci	.name  = MODULENAME,
50662306a36Sopenharmony_ci	.class = "hotkey",
50762306a36Sopenharmony_ci	.ids   = fujitsu_ids,
50862306a36Sopenharmony_ci	.ops   = {
50962306a36Sopenharmony_ci		.add    = acpi_fujitsu_add,
51062306a36Sopenharmony_ci		.remove	= acpi_fujitsu_remove,
51162306a36Sopenharmony_ci	},
51262306a36Sopenharmony_ci	.drv.pm = &acpi_fujitsu_pm,
51362306a36Sopenharmony_ci};
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic int __init fujitsu_module_init(void)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	int error;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	dmi_check_system(dmi_ids);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	error = acpi_bus_register_driver(&acpi_fujitsu_driver);
52262306a36Sopenharmony_ci	if (error)
52362306a36Sopenharmony_ci		return error;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	return 0;
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cistatic void __exit fujitsu_module_exit(void)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	acpi_bus_unregister_driver(&acpi_fujitsu_driver);
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cimodule_init(fujitsu_module_init);
53462306a36Sopenharmony_cimodule_exit(fujitsu_module_exit);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ciMODULE_AUTHOR("Robert Gerlach <khnz@gmx.de>");
53762306a36Sopenharmony_ciMODULE_DESCRIPTION("Fujitsu tablet pc extras driver");
53862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
53962306a36Sopenharmony_ciMODULE_VERSION("2.5");
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, fujitsu_ids);
542