From: Robert Gerlach <khnz@gmx.de>
To: Matthew Garrett <mjg@redhat.com>
Cc: "Ryan H. Lewis" <me@ryanlewis.net>, platform-driver-x86@vger.kernel.org
Subject: [RESEND PATCH] Fujitsu tablet pc extras driver
Date: Tue, 29 Mar 2011 12:03:22 +0200 [thread overview]
Message-ID: <20110329100322.GA7099@fais.local> (raw)
This patch adds support for the tablet buttons und the
"tablet mode" detection for many Fujitsu tablets and
convertable notebooks.
Signed-off-by: Robert Gerlach <khnz@gmx.de>
Reviewed-By: Ryan H. Lewis <me@ryanlewis.net>
diff --git a/MAINTAINERS b/MAINTAINERS
index 8aa1cac..729ee0f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2741,6 +2741,12 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/fujitsu-laptop.c
+FUJITSU TABLET EXTRAS
+M: Robert Gerlach <khnz@gmx.de>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/fujitsu-tablet.c
+
FUSE: FILESYSTEM IN USERSPACE
M: Miklos Szeredi <miklos@szeredi.hu>
L: fuse-devel@lists.sourceforge.net
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 222dfb7..1c1ea8d 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -128,6 +128,18 @@ config FUJITSU_LAPTOP_DEBUG
If you are not sure, say N here.
+config FUJITSU_TABLET
+ tristate "Fujitsu Tablet PC Extras"
+ depends on X86
+ depends on INPUT
+ default n
+ ---help---
+ Say Y here for support of the tablet buttons and the display
+ orientation switch, used on many Fujitsu tablet PCs (like
+ Stylistic ST5xxx, Lifebook P1xxx and Lifebook T-Series).
+
+ If you have a Fujitsu tablet pc, say Y or M here.
+
config TC1100_WMI
tristate "HP Compaq TC1100 Tablet WMI Extras (EXPERIMENTAL)"
depends on !X86_64
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 299aefb..7c8ab7a 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o
+obj-$(CONFIG_FUJITSU_TABLET) += fujitsu-tablet.o
obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
obj-$(CONFIG_ACPI_WMI) += wmi.o
diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c
new file mode 100644
index 0000000..cc08f05
--- /dev/null
+++ b/drivers/platform/x86/fujitsu-tablet.c
@@ -0,0 +1,480 @@
+/* Kernel driver for FSC Tablet PC buttons
+ *
+ * Copyright (C) 2006-2010 Robert Gerlach <khnz@gmx.de>
+ * Copyright (C) 2005-2006 Jan Rychter <jan@rychter.com>
+ *
+ * You can redistribute and/or modify this program under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/dmi.h>
+
+#define MODULENAME "fujitsu-tablet"
+
+#define INTERRUPT 5
+#define IO_BASE 0xfd70
+
+static const struct acpi_device_id fujitsu_ids[] = {
+ { .id = "FUJ02BD" },
+ { .id = "FUJ02BF" },
+ { .id = "" }
+};
+
+struct fujitsu_config {
+ int invert_orientation_bit;
+ unsigned short keymap[16];
+};
+
+static struct fujitsu_config config_Lifebook_Tseries __initconst = {
+ .invert_orientation_bit = 1,
+ .keymap = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_SCROLLDOWN,
+ KEY_SCROLLUP,
+ KEY_DIRECTION,
+ KEY_FN,
+ KEY_BRIGHTNESSUP,
+ KEY_BRIGHTNESSDOWN,
+ KEY_BRIGHTNESS_ZERO,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_F13
+ }
+};
+
+static struct fujitsu_config config_Lifebook_U810 __initconst = {
+ .invert_orientation_bit = 1,
+ .keymap = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_PROG1,
+ KEY_PROG2,
+ KEY_DIRECTION,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_UP,
+ KEY_DOWN,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_FN,
+ KEY_SLEEP
+ }
+};
+
+static struct fujitsu_config config_Stylistic_Tseries __initconst = {
+ .invert_orientation_bit = 0,
+ .keymap = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_PRINT,
+ KEY_BACKSPACE,
+ KEY_SPACE,
+ KEY_ENTER,
+ KEY_BRIGHTNESSUP,
+ KEY_BRIGHTNESSDOWN,
+ KEY_DOWN,
+ KEY_UP,
+ KEY_SCROLLUP,
+ KEY_SCROLLDOWN,
+ KEY_FN
+ }
+};
+
+static struct fujitsu_config config_Stylistic_ST5xxx __initconst = {
+ .invert_orientation_bit = 0,
+ .keymap = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_MAIL,
+ KEY_DIRECTION,
+ KEY_ESC,
+ KEY_ENTER,
+ KEY_BRIGHTNESSUP,
+ KEY_BRIGHTNESSDOWN,
+ KEY_DOWN,
+ KEY_UP,
+ KEY_SCROLLUP,
+ KEY_SCROLLDOWN,
+ KEY_FN,
+ KEY_F13
+ }
+};
+
+static struct { /* fujitsu_t */
+ struct platform_device *pdev;
+ struct input_dev *idev;
+ struct fujitsu_config config;
+ int orientation;
+ unsigned long prev_keymask;
+} fujitsu;
+
+/*** HELPER *******************************************************************/
+
+static inline u8 fujitsu_ack(void)
+{
+ return inb(IO_BASE+2);
+}
+
+static inline u8 fujitsu_status(void)
+{
+ return inb(IO_BASE+6);
+}
+
+static inline u8 fujitsu_read_register(const u8 addr)
+{
+ outb(addr, IO_BASE);
+ return inb(IO_BASE+4);
+}
+
+
+/*** INPUT ********************************************************************/
+
+static int __devinit input_fujitsu_setup(struct device *dev)
+{
+ struct input_dev *idev;
+ int error;
+ int x;
+
+ idev = input_allocate_device();
+ if (!idev)
+ return -ENOMEM;
+
+ idev->dev.parent = dev;
+ idev->phys = KBUILD_MODNAME "/input0";
+ idev->name = "Fujitsu tablet buttons";
+ idev->id.bustype = BUS_HOST;
+ idev->id.vendor = 0x1734; /* Fujitsu Siemens Computer GmbH */
+ idev->id.product = 0x0001;
+ idev->id.version = 0x0101;
+
+ idev->keycode = fujitsu.config.keymap;
+ idev->keycodesize = sizeof(fujitsu.config.keymap[0]);
+ idev->keycodemax = ARRAY_SIZE(fujitsu.config.keymap);
+
+ __set_bit(EV_REP, idev->evbit);
+ __set_bit(EV_KEY, idev->evbit);
+
+ for (x = 0; x < ARRAY_SIZE(fujitsu.config.keymap); x++)
+ if (fujitsu.config.keymap[x])
+ __set_bit(fujitsu.config.keymap[x], idev->keybit);
+
+ __set_bit(EV_MSC, idev->evbit);
+ __set_bit(MSC_SCAN, idev->mscbit);
+
+ __set_bit(EV_SW, idev->evbit);
+ __set_bit(SW_TABLET_MODE, idev->swbit);
+
+ error = input_register_device(idev);
+ if (error) {
+ input_free_device(idev);
+ return error;
+ }
+
+ fujitsu.idev = idev;
+ return 0;
+}
+
+static void input_fujitsu_remove(void)
+{
+ if (fujitsu.idev)
+ input_unregister_device(fujitsu.idev);
+}
+
+static void fujitsu_report_orientation(void)
+{
+ struct input_dev *idev = fujitsu.idev;
+ int orientation = fujitsu_read_register(0xdd);
+
+ if (orientation & 0x02) {
+ orientation ^= fujitsu.config.invert_orientation_bit;
+ orientation &= 0x01;
+
+ if (orientation != fujitsu.orientation) {
+ input_report_switch(idev, SW_TABLET_MODE,
+ fujitsu.orientation = orientation);
+ input_sync(idev);
+ }
+ }
+}
+
+static void fujitsu_report_key(void)
+{
+ unsigned long keymask;
+ unsigned long changed;
+
+ keymask = fujitsu_read_register(0xde);
+ keymask |= fujitsu_read_register(0xdf) << 8;
+ keymask ^= 0xffff;
+
+ changed = keymask ^ fujitsu.prev_keymask;
+
+ if (changed) {
+ int keycode, pressed;
+ int x = 0;
+
+ /* save current state and filter not changed bits */
+ fujitsu.prev_keymask = keymask;
+
+ /* looking for the location of the first bit which is set */
+ while (!test_bit(x, &changed))
+ x++;
+
+ keycode = fujitsu.config.keymap[x];
+ pressed = !!(keymask & changed);
+
+ if (pressed)
+ input_event(fujitsu.idev, EV_MSC, MSC_SCAN, x);
+
+ input_report_key(fujitsu.idev, keycode, pressed);
+ input_sync(fujitsu.idev);
+ }
+}
+
+
+/*** INTERRUPT ****************************************************************/
+
+static irqreturn_t fujitsu_isr(int irq, void *dev_id)
+{
+ if (!(fujitsu_status() & 0x01))
+ return IRQ_NONE;
+
+ fujitsu_report_orientation();
+ fujitsu_report_key();
+ fujitsu_ack();
+
+ return IRQ_HANDLED;
+}
+
+
+/*** DEVICE *******************************************************************/
+
+static int fujitsu_busywait(void)
+{
+ int timeout_counter = 50;
+
+ while (fujitsu_status() & 0x02 && --timeout_counter)
+ msleep(20);
+
+ return !timeout_counter;
+}
+
+static void fujitsu_reset(void)
+{
+ fujitsu_ack();
+ if (fujitsu_busywait())
+ printk(KERN_WARNING MODULENAME ": timeout, real reset needed!\n");
+}
+
+static int __devinit fujitsu_probe(struct platform_device *pdev)
+{
+ int error;
+
+ error = input_fujitsu_setup(&pdev->dev);
+ if (error)
+ goto err_input;
+
+ if (!request_region(IO_BASE, 8, MODULENAME)) {
+ dev_err(&pdev->dev, "region 0x%04x busy\n", IO_BASE);
+ error = -EBUSY;
+ goto err_input;
+ }
+
+ fujitsu_reset();
+
+ fujitsu_report_orientation();
+ input_sync(fujitsu.idev);
+
+ error = request_irq(INTERRUPT, fujitsu_isr,
+ IRQF_SHARED, MODULENAME, fujitsu_isr);
+ if (error) {
+ dev_err(&pdev->dev, "unable to get irq %d\n", INTERRUPT);
+ goto err_io;
+ }
+
+ return 0;
+
+err_io:
+ release_region(IO_BASE, 8);
+err_input:
+ input_fujitsu_remove();
+ return error;
+}
+
+static int __devexit fujitsu_remove(struct platform_device *pdev)
+{
+ free_irq(INTERRUPT, fujitsu_isr);
+ release_region(IO_BASE, 8);
+ input_fujitsu_remove();
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int fujitsu_resume(struct platform_device *pdev)
+{
+ fujitsu_reset();
+ fujitsu_report_orientation();
+ return 0;
+}
+#else
+#define fujitsu_resume NULL
+#endif
+
+static struct platform_driver fujitsu_platform_driver = {
+ .driver = {
+ .name = MODULENAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = fujitsu_probe,
+ .remove = __devexit_p(fujitsu_remove),
+ .resume = fujitsu_resume,
+};
+
+
+/*** DMI **********************************************************************/
+
+static int __init fujitsu_dmi_matched(const struct dmi_system_id *dmi)
+{
+ printk(KERN_INFO MODULENAME ": %s detected\n", dmi->ident);
+ memcpy(&fujitsu.config, dmi->driver_data,
+ sizeof(struct fujitsu_config));
+ return 1;
+}
+
+static struct dmi_system_id dmi_ids[] __initdata = {
+ {
+ .callback = fujitsu_dmi_matched,
+ .ident = "Fujitsu Siemens P/T Series",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK")
+ },
+ .driver_data = &config_Lifebook_Tseries
+ },
+ {
+ .callback = fujitsu_dmi_matched,
+ .ident = "Fujitsu Lifebook T Series",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook T")
+ },
+ .driver_data = &config_Lifebook_Tseries
+ },
+ {
+ .callback = fujitsu_dmi_matched,
+ .ident = "Fujitsu Siemens Stylistic T Series",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Stylistic T")
+ },
+ .driver_data = &config_Stylistic_Tseries
+ },
+ {
+ .callback = fujitsu_dmi_matched,
+ .ident = "Fujitsu LifeBook U810",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook U810")
+ },
+ .driver_data = &config_Lifebook_U810
+ },
+ {
+ .callback = fujitsu_dmi_matched,
+ .ident = "Fujitsu Siemens Stylistic ST5xxx Series",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "STYLISTIC ST5")
+ },
+ .driver_data = &config_Stylistic_ST5xxx
+ },
+ {
+ .callback = fujitsu_dmi_matched,
+ .ident = "Fujitsu Siemens Stylistic ST5xxx Series",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Stylistic ST5")
+ },
+ .driver_data = &config_Stylistic_ST5xxx
+ },
+ {
+ .callback = fujitsu_dmi_matched,
+ .ident = "Unknown (using defaults)",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, ""),
+ DMI_MATCH(DMI_PRODUCT_NAME, "")
+ },
+ .driver_data = &config_Lifebook_Tseries
+ },
+ { NULL }
+};
+
+
+/*** MODULE *******************************************************************/
+
+static int __init fujitsu_module_init(void)
+{
+ int error;
+
+ dmi_check_system(dmi_ids);
+
+ error = platform_driver_register(&fujitsu_platform_driver);
+ if (error)
+ return error;
+
+ fujitsu.pdev = platform_device_register_simple(MODULENAME, -1, NULL, 0);
+ if (IS_ERR(fujitsu.pdev)) {
+ error = PTR_ERR(fujitsu.pdev);
+ platform_driver_unregister(&fujitsu_platform_driver);
+ return error;
+ }
+
+ return 0;
+}
+
+static void __exit fujitsu_module_exit(void)
+{
+ platform_device_unregister(fujitsu.pdev);
+ platform_driver_unregister(&fujitsu_platform_driver);
+}
+
+module_init(fujitsu_module_init);
+module_exit(fujitsu_module_exit);
+
+MODULE_AUTHOR("Robert Gerlach <khnz@gmx.de>");
+MODULE_DESCRIPTION("Fujitsu Siemens tablet button driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("git");
+
+MODULE_DEVICE_TABLE(acpi, fujitsu_ids);
next reply other threads:[~2011-03-29 10:03 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-03-29 10:03 Robert Gerlach [this message]
2011-03-29 10:33 ` [RESEND PATCH] Fujitsu tablet pc extras driver Matthew Garrett
2011-03-29 12:11 ` Robert Gerlach
2011-03-29 12:13 ` Matthew Garrett
2011-03-31 5:50 ` Dmitry Torokhov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20110329100322.GA7099@fais.local \
--to=khnz@gmx.de \
--cc=me@ryanlewis.net \
--cc=mjg@redhat.com \
--cc=platform-driver-x86@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox