All of lore.kernel.org
 help / color / mirror / Atom feed
* [RESEND PATCH] Fujitsu tablet pc extras driver
@ 2011-03-29 10:03 Robert Gerlach
  2011-03-29 10:33 ` Matthew Garrett
  2011-03-31  5:50 ` Dmitry Torokhov
  0 siblings, 2 replies; 5+ messages in thread
From: Robert Gerlach @ 2011-03-29 10:03 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: Ryan H. Lewis, platform-driver-x86

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);

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [RESEND PATCH] Fujitsu tablet pc extras driver
  2011-03-29 10:03 [RESEND PATCH] Fujitsu tablet pc extras driver Robert Gerlach
@ 2011-03-29 10:33 ` Matthew Garrett
  2011-03-29 12:11   ` Robert Gerlach
  2011-03-31  5:50 ` Dmitry Torokhov
  1 sibling, 1 reply; 5+ messages in thread
From: Matthew Garrett @ 2011-03-29 10:33 UTC (permalink / raw)
  To: Robert Gerlach; +Cc: Ryan H. Lewis, platform-driver-x86, dmitry.torokhov

On Tue, Mar 29, 2011 at 12:03:22PM +0200, Robert Gerlach wrote:

> +#define INTERRUPT 5
> +#define IO_BASE 0xfd70

You're binding to an ACPI device here - does it have a _CRS method? If 
so, you should retrieve the resource information from acpipnp rather 
than hardcoding it. That'll involve reworking it as an acpi driver 
rather than a platform one, but that should be easy enough.

> +/*** HELPER *******************************************************************/

Does checkpatch really not complain about that? I don't think it's a 
terribly helpful comment in any case :)

> +static int fujitsu_busywait(void)

You're sleeping, so it's not really a busywait...

> +static void fujitsu_reset(void)
> +{
> +	fujitsu_ack();
> +	if (fujitsu_busywait())
> +		printk(KERN_WARNING MODULENAME ": timeout, real reset needed!\n");
> +}

We have no idea how to do a "real" reset, I guess?

> +	error = request_irq(INTERRUPT, fujitsu_isr,
> +			IRQF_SHARED, MODULENAME, fujitsu_isr);

Any risk of the irq firing between you doing setup and requesting the 
IRQ?

Other than the above, this looks fine from the driver point of view - 
Cc:ing Dmitry so he can have a quick look at the input side of things.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RESEND PATCH] Fujitsu tablet pc extras driver
  2011-03-29 10:33 ` Matthew Garrett
@ 2011-03-29 12:11   ` Robert Gerlach
  2011-03-29 12:13     ` Matthew Garrett
  0 siblings, 1 reply; 5+ messages in thread
From: Robert Gerlach @ 2011-03-29 12:11 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: Ryan H. Lewis, platform-driver-x86

On Tue, Mar 29, 2011 at 11:33:22AM +0100, Matthew Garrett wrote:
> On Tue, Mar 29, 2011 at 12:03:22PM +0200, Robert Gerlach wrote:
> 
> > +#define INTERRUPT 5
> > +#define IO_BASE 0xfd70
> 
> You're binding to an ACPI device here - does it have a _CRS method? If 
> so, you should retrieve the resource information from acpipnp rather 
> than hardcoding it. That'll involve reworking it as an acpi driver 
> rather than a platform one, but that should be easy enough.

Yes, it have a _CRS, but I removed the code because it is always the same
IRQ and IO range. I'll reimplement with the first device it make it
necessary.

> > +/*** HELPER *******************************************************************/
> 
> Does checkpatch really not complain about that? I don't think it's a 
> terribly helpful comment in any case :)

True, I'll remove it.

> > +static int fujitsu_busywait(void)
> 
> You're sleeping, so it's not really a busywait...
> 
> > +static void fujitsu_reset(void)
> > +{
> > +	fujitsu_ack();
> > +	if (fujitsu_busywait())
> > +		printk(KERN_WARNING MODULENAME ": timeout, real reset needed!\n");
> > +}
> 
> We have no idea how to do a "real" reset, I guess?

No, I'm not sure if it possible to reset the device. But in over 4 years,
I never see this happen. But I want to keep it, maybe I need to
reinvestigate someday.

> > +	error = request_irq(INTERRUPT, fujitsu_isr,
> > +			IRQF_SHARED, MODULENAME, fujitsu_isr);
> 
> Any risk of the irq firing between you doing setup and requesting the 
> IRQ?

Theoretically yes. I will check it.

> Other than the above, this looks fine from the driver point of view - 
> Cc:ing Dmitry so he can have a quick look at the input side of things.

I'll make a new patch.

Thank you.

> -- 
> Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RESEND PATCH] Fujitsu tablet pc extras driver
  2011-03-29 12:11   ` Robert Gerlach
@ 2011-03-29 12:13     ` Matthew Garrett
  0 siblings, 0 replies; 5+ messages in thread
From: Matthew Garrett @ 2011-03-29 12:13 UTC (permalink / raw)
  To: Robert Gerlach; +Cc: Ryan H. Lewis, platform-driver-x86

On Tue, Mar 29, 2011 at 02:11:03PM +0200, Robert Gerlach wrote:
> On Tue, Mar 29, 2011 at 11:33:22AM +0100, Matthew Garrett wrote:
> > You're binding to an ACPI device here - does it have a _CRS method? If 
> > so, you should retrieve the resource information from acpipnp rather 
> > than hardcoding it. That'll involve reworking it as an acpi driver 
> > rather than a platform one, but that should be easy enough.
> 
> Yes, it have a _CRS, but I removed the code because it is always the same
> IRQ and IO range. I'll reimplement with the first device it make it
> necessary.

Chances are that it'll always be that, but it's better to use the 
firmware data rather than hardcoding.

Thanks!
-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RESEND PATCH] Fujitsu tablet pc extras driver
  2011-03-29 10:03 [RESEND PATCH] Fujitsu tablet pc extras driver Robert Gerlach
  2011-03-29 10:33 ` Matthew Garrett
@ 2011-03-31  5:50 ` Dmitry Torokhov
  1 sibling, 0 replies; 5+ messages in thread
From: Dmitry Torokhov @ 2011-03-31  5:50 UTC (permalink / raw)
  To: Robert Gerlach; +Cc: Matthew Garrett, Ryan H. Lewis, platform-driver-x86

Hi Robert,

On Tue, Mar 29, 2011 at 12:03:22PM +0200, Robert Gerlach wrote:
> 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>
> 

Just a few random notes...

>  
> +config FUJITSU_TABLET
> +	tristate "Fujitsu Tablet PC Extras"
> +	depends on X86
> +	depends on INPUT
> +	default n

No need to say "default n" as it is default ;)

> +
> +struct fujitsu_config {
> +	int invert_orientation_bit;

bool?

> +	unsigned short keymap[16];
> +};
> +
> +static struct fujitsu_config config_Lifebook_Tseries __initconst = {
> +	.invert_orientation_bit = 1,

= true

> +	.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

Anything better than F13? What is the key supposed to do?

> +
> +static struct fujitsu_config config_Stylistic_Tseries __initconst = {
> +	.invert_orientation_bit = 0,

= false or simply omit.

> +
> +static struct {						/* fujitsu_t */
> +	struct platform_device *pdev;
> +	struct input_dev *idev;
> +	struct fujitsu_config config;
> +	int orientation;

bool.

> +
> +/*** 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++;
> +

find_first_bit()? BTW, can we have several keys reported at once?

> +		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

Convert to dev_pm_ops, have fujitsu_resume() guarded by CONFIG_PM_SLEEP
and then do:

static SIMPLE_DEV_PM_OPS(fujitsu_pm_ops, NULL, fujitsu_resume);

> +
> +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);

Do you really want to continue if you don't find suitable DMI entry?

> +
> +	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);

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2011-03-31  5:50 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-03-29 10:03 [RESEND PATCH] Fujitsu tablet pc extras driver Robert Gerlach
2011-03-29 10:33 ` Matthew Garrett
2011-03-29 12:11   ` Robert Gerlach
2011-03-29 12:13     ` Matthew Garrett
2011-03-31  5:50 ` Dmitry Torokhov

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.