Skip to contentSkip to conversation

Fujitsu tablet pc extras driver

PATCH RESEND In review Robert Gerlach 3 participants 5 messages Mar 29, 2011 – Mar 31, 2011

Subsystems (2)
lore
Share & tools
lore

Robert Gerlach

author maintainer
#0 lore raw

PATCH RESEND 1/1 Fujitsu tablet pc extras driver

This patch adds support for the tablet buttons und the "tablet mode" detection for many Fujitsu tablets and convertable notebooks.

MAINTAINERS +6 -0
index 8aa1cac..729ee0f 100644
@@ -2741,6 +2741,12 @@ L:	[email protected]
 S:	Maintained
 F:	drivers/platform/x86/fujitsu-laptop.c
 
+FUJITSU TABLET EXTRAS
+M:	Robert Gerlach <[email protected]>
+L:	[email protected]
+S:	Maintained
+F:	drivers/platform/x86/fujitsu-tablet.c
+
 FUSE: FILESYSTEM IN USERSPACE
 M:	Miklos Szeredi <[email protected]>
 L:	[email protected]
drivers/platform/x86/Kconfig +12 -0
index 222dfb7..1c1ea8d 100644
@@ -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
drivers/platform/x86/Makefile +1 -0
index 299aefb..7c8ab7a 100644
@@ -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
drivers/platform/x86/fujitsu-tablet.c new file +480 -0

480 lines not on this page · raw message

#1 lore raw

drivers/platform/x86/fujitsu-tablet.c line 36

Quoted 2 lines · Robert Gerlach
drivers/platform/x86/fujitsu-tablet.c new file +2 -0
+#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.

Quoted 1 line

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

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

Quoted 1 line

+static int fujitsu_busywait(void)

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

drivers/platform/x86/fujitsu-tablet.c line 299

Quoted 6 lines
drivers/platform/x86/fujitsu-tablet.c new file +6 -0
+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?

drivers/platform/x86/fujitsu-tablet.c line 321

Quoted 2 lines
drivers/platform/x86/fujitsu-tablet.c new file +2 -0
+	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.

Signature · 1 line
Matthew Garrett | [email protected]
author maintainer
#2 lore raw
Quoted 9 lines · Matthew Garrett

drivers/platform/x86/fujitsu-tablet.c line 36

Quoted 2 lines · Robert Gerlach
drivers/platform/x86/fujitsu-tablet.c new file +2 -0
+#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.

Quoted 4 lines
Quoted 1 line

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

Quoted 4 lines
Quoted 1 line

+static int fujitsu_busywait(void)

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

Quoted 8 lines

drivers/platform/x86/fujitsu-tablet.c line 299

Quoted 6 lines
drivers/platform/x86/fujitsu-tablet.c new file +6 -0
+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.

Quoted 5 lines

drivers/platform/x86/fujitsu-tablet.c line 321

Quoted 2 lines
drivers/platform/x86/fujitsu-tablet.c new file +2 -0
+	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.

Quoted 2 lines

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.

Quoted 2 lines
Signature · 1 line
Matthew Garrett | [email protected]
#3 lore raw
Quoted 9 lines · Robert Gerlach
Quoted 4 lines · Matthew Garrett

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!

Signature · 1 line
Matthew Garrett | [email protected]
#4 lore raw
In reply to Robert Gerlach

Hi Robert,

Quoted 7 lines · Robert Gerlach

This patch adds support for the tablet buttons und the "tablet mode" detection for many Fujitsu tablets and convertable notebooks.

Just a few random notes...

drivers/platform/x86/Kconfig config FUJITSU_LAPTOP_DEBUG · line 135

Quoted 6 lines
drivers/platform/x86/Kconfig +5 -0
+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 ;)

drivers/platform/x86/fujitsu-tablet.c line 45

Quoted 3 lines
drivers/platform/x86/fujitsu-tablet.c new file +3 -0
+
+struct fujitsu_config {
+	int invert_orientation_bit;

bool?

drivers/platform/x86/fujitsu-tablet.c line 50

Quoted 5 lines
drivers/platform/x86/fujitsu-tablet.c new file +5 -0
+	unsigned short keymap[16];
+};
+
+static struct fujitsu_config config_Lifebook_Tseries __initconst = {
+	.invert_orientation_bit = 1,

= true

Quoted 7 lines
drivers/platform/x86/fujitsu-tablet.c new file +7 -0
+	.keymap = {
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_SCROLLDOWN,
+		KEY_SCROLLUP,

drivers/platform/x86/fujitsu-tablet.c line 67

Quoted 10 lines
drivers/platform/x86/fujitsu-tablet.c new file +10 -0
+		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?

drivers/platform/x86/fujitsu-tablet.c line 94

Quoted 3 lines
drivers/platform/x86/fujitsu-tablet.c new file +3 -0
+
+static struct fujitsu_config config_Stylistic_Tseries __initconst = {
+	.invert_orientation_bit = 0,

= false or simply omit.

drivers/platform/x86/fujitsu-tablet.c line 140

Quoted 6 lines
drivers/platform/x86/fujitsu-tablet.c new file +6 -0
+
+static struct {						/* fujitsu_t */
+	struct platform_device *pdev;
+	struct input_dev *idev;
+	struct fujitsu_config config;
+	int orientation;

bool.

Quoted 83 lines
drivers/platform/x86/fujitsu-tablet.c new file +83 -0
+
+/*** 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) {

drivers/platform/x86/fujitsu-tablet.c line 254

Quoted 10 lines
drivers/platform/x86/fujitsu-tablet.c new file +10 -0
+		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?

Quoted 89 lines
drivers/platform/x86/fujitsu-tablet.c new file +89 -0
+		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;
+}
+

drivers/platform/x86/fujitsu-tablet.c line 353

Quoted 10 lines
drivers/platform/x86/fujitsu-tablet.c new file +10 -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);

Quoted 87 lines
drivers/platform/x86/fujitsu-tablet.c new file +87 -0
+
+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 }

drivers/platform/x86/fujitsu-tablet.c line 450

Quoted 10 lines
drivers/platform/x86/fujitsu-tablet.c new file +10 -0
+};
+
+
+/*** 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?

Quoted 20 lines
drivers/platform/x86/fujitsu-tablet.c new file +20 -0
+
+	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);
+}

drivers/platform/x86/fujitsu-tablet.c line 480

Quoted 10 lines
drivers/platform/x86/fujitsu-tablet.c new file +10 -0
+
+module_init(fujitsu_module_init);
+module_exit(fujitsu_module_exit);
+
+MODULE_AUTHOR("Robert Gerlach <[email protected]>");
+MODULE_DESCRIPTION("Fujitsu Siemens tablet button driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("git");
+
+MODULE_DEVICE_TABLE(acpi, fujitsu_ids);

Thanks.

Signature · 1 line
Dmitry