Linux Input/HID development
 help / color / mirror / Atom feed
* Re: [PATCH 2/2] HID: kye: fix unresponsive keyboard
From: Jiri Kosina @ 2013-11-21  9:30 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Benjamin Tissoires, Adam Kulagowski, linux-input, linux-kernel
In-Reply-To: <1384958982-23099-2-git-send-email-benjamin.tissoires@redhat.com>

On Wed, 20 Nov 2013, Benjamin Tissoires wrote:

> The manticore keyboard requires that all usb EP are opened at least
> once to be fully functional. The third EP forwards to the user space
> some vendor specific information about the keyboard state, but are useless
> currently for the kernel.
> 
> Opening them and closing them makes the keyboard responsive again.
> 
> Reported-and-tested-by: Adam Kulagowski <fidor@fidor.org>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> ---
> 
> Hi Jiri,
> 
> well, this one was a long shot as I do not have the hardware to actually
> spot the exact call which enables the keyboard. Adam told me that just
> opening and closing once all the /dev/hidrawX attached to the keyboard makes
> the keyboard functional, so, here is the kernel version of this.
> Anyway, it just works, so I think we can take this one as well.

Man, this is ugly :)

What can we do anyway. Applied, thanks.

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply

* Re: [appleir] BUG: unable to handle kernel NULL pointer dereference
From: Luis Henriques @ 2013-11-21 10:13 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: James Henstridge, Benjamin Tissoires, linux-kernel, linux-input,
	Fabien André, Bastien Nocera
In-Reply-To: <alpine.LNX.2.00.1311210959170.22082@pobox.suse.cz>

On Thu, Nov 21, 2013 at 09:59:27AM +0100, Jiri Kosina wrote:
> On Thu, 21 Nov 2013, James Henstridge wrote:
> 
> > Sorry for the delays in testing out the patch.  I have tried a kernel
> > with the patch applied, and can no longer reproduce the oops.  The
> > hid-appleir driver appears to be working correctly, generating key
> > press events in response to the remote, and LIRC functions correctly
> > via hiddev.
> > 
> > Thanks for the everyone's help with this.
> 
> Applied, thanks.

Hi Jiri,

Since this fixes an issue in a 3.11 kernel, could you please tag this
commit for stable>=3.11?  If its too late, I can send the request to
stable@ once this patch is merged.

Cheers,
--
Luis

^ permalink raw reply

* Re: [PATCH 1/3] Only process ABS_MT_SLOT where there are slots available
From: Antonio Ospite @ 2013-11-21 10:25 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Benjamin Tissoires, Benjamin Tissoires, Jiri Kosina,
	David Herrmann, Henrik Rydberg, simon, case, linux-input,
	linux-kernel
In-Reply-To: <20131121045620.GA4702@core.coreip.homeip.net>

On Wed, 20 Nov 2013 20:56:21 -0800
Dmitry Torokhov <dmitry.torokhov@gmail.com> wrote:

> Hi Benjamin, Antonio,
> 
> On Wed, Nov 20, 2013 at 04:32:19PM -0500, Benjamin Tissoires wrote:
> > From: Antonio Ospite <ospite@studenti.unina.it>
> > 
> > This fixes the case when a non-multitouch device happens to have a HID
> > code equal to ABS_MT_SLOT, like the Sony Sixaxis has for the left dpad
> > analog control.
> > 
> > Updated to latest tree by Benjamin Tissoires.
> 
> I do not think this is a proper way to address the issue. Generic HID
> driver should not encroach onto multitouch ABS range and either stop
> mapping absolute axis or map them properly.
> 
> Thanks.

Dmitry I'd agree with you in principle, and I trusted your judgment on
that in 2011 too when I first came up with this "hack", and in fact I
didn't insist to have it merged, but then I failed to properly address
the issue and users were left with a non functional device for a long
time.

Now I've got some more experience, I know how to do it, and if you
decide to drop Benjamin's _defensive_ solution I will be more than
happy to remap the axes of the joypad to fix the issue the way you like
it, the way _we_ like it.

But there were some concerns about backward compatibility:
http://www.spinics.net/lists/linux-input/msg28280.html and following.

My position is egoistical here:

  - if you will accept a patch which remaps the keys to match the
    gamepad API, even breaking the current interface I'd say we go for
    that (the current keymap is not fully working anyway because of
    the ABS_MT issue).

  - if really "we do not break user space" no matter what, then
    Benjamin's patch is OK by me.

I think I see what your fear is here: if there is such defensive
fallback mechanism in place there will be no motivation to enforce
proper fixes by driver authors at the HID level, am I right?

So Benjamin, an alternative could be to spit out warnings when a non MT
device uses MT codes and curse at lazy developers like me in the logs.
What about that?

Ciao,
   Antonio

-- 
Antonio Ospite
http://ao2.it

A: Because it messes up the order in which people normally read text.
   See http://en.wikipedia.org/wiki/Posting_style
Q: Why is top-posting such a bad thing?

^ permalink raw reply

* Re: [PATCH 0/3] Fixes for multitouch / generic gamepads interaction
From: Henrik Rydberg @ 2013-11-21 12:38 UTC (permalink / raw)
  To: Benjamin Tissoires, Benjamin Tissoires, Jiri Kosina,
	Dmitry Torokhov, David Herrmann, Antonio Ospite, simon, case,
	linux-input, linux-kernel
In-Reply-To: <1384983141-31019-1-git-send-email-benjamin.tissoires@redhat.com>

Hi Benjamin,

> I have been reported recently a problem with the Sixaxis controller
> (http://www.spinics.net/lists/linux-input/msg28098.html).
> The root of the problem comes from hid-input, which maps unknown axis to
> ABS_MISC. However, when an event code is already in use, hid-input uses the one
> after, leading to uses of ABS_MISC + N, where N is the number of unknown axis.

Yes, and clearly we should not do that. :-)

> We are encountering a problem with the multitouch protocol here because if a
> device has more than 7 unknown axis (which is the case for the PS3 Sixaxis
> controller), then the unknown axis get maps to ABS_MT_SLOT and beyond.
> 
> This infers two problems:
> - the axis currently mapped on ABS_MT_SLOT is a special case in the kernel,
> and it is not updated
> - the axis after ABS_MT_SLOT are not filtered anymore, as the kernel things
> the device is using a multitouch protocol A.

The problem is generic, and ABS_MT_SLOT just happens to be the first value that
the hid logic hits. When sevenaxis comes out, we have to do this all over again.

> The patch 0001 solves the first problem, whereas the patches 0002 and 0003
> fix the second. Bonus point: the userspace is now correctly notified that one of
> the multitouch protocols is in used, so it does not have to rely on bad designed
> heuristics.

The bad heuristics are lines 918 and onwards in hid-input.c. I am sure we can
come up with a way to allocate those axes in a much less fragile way.

Thanks,
Henrik


^ permalink raw reply

* Re: [PATCH 1/3] Only process ABS_MT_SLOT where there are slots available
From: Benjamin Tissoires @ 2013-11-21 15:53 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Benjamin Tissoires, Jiri Kosina, David Herrmann, Henrik Rydberg,
	Antonio Ospite, simon, case, linux-input, linux-kernel
In-Reply-To: <20131121045620.GA4702@core.coreip.homeip.net>

On 20/11/13 23:56, Dmitry Torokhov wrote:
> Hi Benjamin, Antonio,
> 
> On Wed, Nov 20, 2013 at 04:32:19PM -0500, Benjamin Tissoires wrote:
>> From: Antonio Ospite <ospite@studenti.unina.it>
>>
>> This fixes the case when a non-multitouch device happens to have a HID
>> code equal to ABS_MT_SLOT, like the Sony Sixaxis has for the left dpad
>> analog control.
>>
>> Updated to latest tree by Benjamin Tissoires.
> 
> I do not think this is a proper way to address the issue. Generic HID
> driver should not encroach onto multitouch ABS range and either stop
> mapping absolute axis or map them properly.
> 

Ok, I'm a little bit lost here. Back in May, you told us not to change
the previous mapping for legacy devices:
http://www.spinics.net/lists/linux-input/msg25651.html

And the concern I have is the history made all this things a mess:
- the hid-input.c bad mapping (line 918) is there since at least 2006
(2.6.20) -> dde5845a529ff753364a6d1aea61180946270bfa
- the PS3 Sixasis has been introduced in 2008-10-14 (2.6.28) ->
bd28ce008bdc68ef5902f68d2d62cbb7fa78c415
- the mt protocol A has been committed in 2010-04-28 (2.6.30) ->
5e5ee686e3c0f8a3cbe9b75c2690326bf91af10d
- the mt protocol B has been committed in 2010-07-15 (2.6.36) ->
40d007e7df1dab17bf1ecf91e718218354d963d7

So basically, the PS3 controller existed before we changed the semantic
of its axis. We already had users at that time, and we missed the
overlapping when we introduced the mt protocol.
Then the second mt protocol broke even more the PS3 controller.

What should be fixed now? Because I am sure that there may be other
existing controllers, which were produced and used before 2010 that have
more than 7 unmapped axis.

Cheers,
Benjamin

^ permalink raw reply

* [RFC][PATCH] Input : xpad : Fix Blinking XBOX 360 Gamepad Blinking Leds.
From: Mayeul Cantan @ 2013-11-21 19:55 UTC (permalink / raw)
  To: linux-input, chf.fritz

Hello,
This patch should allow the wireless xbox 360 controllers to display the 
correct controller ID instead of keeping blinking.
Since there are four "channels" on the Microsoft receiver, the 
controller displays the channel it uses,
so if both a wired and a wireless controller are attached to the 
computer, both will sow the ID #1.
I didn't have the opportunity to test this behavior on Microsoft 
Windows, so I need you to comment on this.

CHANGES :
Just cleaned the code which wasn't working correctly, and added some to 
make it work.
Corrected what I think was a typo line 143.
Added something like printk(KERN_INFO "Wireless controller %d 
(dis)connected\n" ),
I don't know if this is a good practice, so I need some feedback on this 
too.

This patch can be applied against the code since commit 
ed06349fe8d12dcb718984862b6e839fc8606c34 (3 months ago).

Thank you,

Mayeul

diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 75e3b10..5f78604 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -140,7 +140,7 @@
  	{ 0x0738, 0x4728, "Mad Catz Street Fighter IV FightPad", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
  	{ 0x0738, 0x4738, "Mad Catz Wired Xbox 360 Controller (SFIV)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
  	{ 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
-	{ 0x0738, 0xbeef, "Mad Catz JOYTECH NEO SE Advanced GamePad", XTYPE_XBOX360 },
+	{ 0x0738, 0xbeef, "Mad Catz JOYTECH NEO SE Advanced GamePad", 0, XTYPE_XBOX360 },
  	{ 0x0c12, 0x8802, "Zeroplus Xbox Controller", 0, XTYPE_XBOX },
  	{ 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", DANCEPAD_MAP_CONFIG, XTYPE_XBOX },
  	{ 0x0c12, 0x880a, "Pelican Eclipse PL-2023", 0, XTYPE_XBOX },
@@ -274,9 +274,6 @@ struct usb_xpad {
  	unsigned char *idata;		/* input data */
  	dma_addr_t idata_dma;
  
-	struct urb *bulk_out;
-	unsigned char *bdata;
-
  #if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS)
  	struct urb *irq_out;		/* urb for interrupt out report */
  	unsigned char *odata;		/* output data */
@@ -451,15 +448,19 @@ static void xpad360_process_packet(struct usb_xpad *xpad,
   *
   */
  
+static void xpad_send_led_command(struct usb_xpad *xpad, int command); //needed just after
  static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
  {
  	/* Presence change */
  	if (data[0] & 0x08) {
  		if (data[1] & 0x80) {
  			xpad->pad_present = 1;
-			usb_submit_urb(xpad->bulk_out, GFP_ATOMIC);
-		} else
+			xpad_send_led_command(xpad, xpad->intf->cur_altsetting->desc.bInterfaceNumber /2 + 66);
+			printk(KERN_INFO "Wireless controller %d connected\n",xpad->intf->cur_altsetting->desc.bInterfaceNumber /2 + 1);
+		} else {
+			printk(KERN_INFO "Wireless controller %d disconnected\n",xpad->intf->cur_altsetting->desc.bInterfaceNumber /2 + 1);
  			xpad->pad_present = 0;
+		}
  	}
  
  	/* Valid pad data */
@@ -512,28 +513,6 @@ static void xpad_irq_in(struct urb *urb)
  			__func__, retval);
  }
  
-static void xpad_bulk_out(struct urb *urb)
-{
-	struct usb_xpad *xpad = urb->context;
-	struct device *dev = &xpad->intf->dev;
-
-	switch (urb->status) {
-	case 0:
-		/* success */
-		break;
-	case -ECONNRESET:
-	case -ENOENT:
-	case -ESHUTDOWN:
-		/* this urb is terminated, clean up */
-		dev_dbg(dev, "%s - urb shutting down with status: %d\n",
-			__func__, urb->status);
-		break;
-	default:
-		dev_dbg(dev, "%s - nonzero urb status received: %d\n",
-			__func__, urb->status);
-	}
-}
-
  #if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS)
  static void xpad_irq_out(struct urb *urb)
  {
@@ -714,7 +693,7 @@ struct xpad_led {
  
  static void xpad_send_led_command(struct usb_xpad *xpad, int command)
  {
-	if (command >= 0 && command < 14) {
+	if (command >= 0 && command < 14 && xpad->xtype == XTYPE_XBOX360) {
  		mutex_lock(&xpad->odata_mutex);
  		xpad->odata[0] = 0x01;
  		xpad->odata[1] = 0x03;
@@ -723,6 +702,16 @@ static void xpad_send_led_command(struct usb_xpad *xpad, int command)
  		usb_submit_urb(xpad->irq_out, GFP_KERNEL);
  		mutex_unlock(&xpad->odata_mutex);
  	}
+	if (xpad->xtype == XTYPE_XBOX360W) {
+		mutex_lock(&xpad->odata_mutex);
+		xpad->odata[0] = 0x00;
+		xpad->odata[1] = 0x00;
+		xpad->odata[2] = 0x08;
+		xpad->odata[3] = command;
+		xpad->irq_out->transfer_buffer_length = 4;
+		usb_submit_urb(xpad->irq_out, GFP_KERNEL);
+		mutex_unlock(&xpad->odata_mutex);
+	}
  }
  
  static void xpad_led_set(struct led_classdev *led_cdev,
@@ -783,6 +772,7 @@ static void xpad_led_disconnect(struct usb_xpad *xpad)
  	}
  }
  #else
+static void xpad_send_led_command(struct usb_xpad *xpad, int command) { }
  static int xpad_led_probe(struct usb_xpad *xpad) { return 0; }
  static void xpad_led_disconnect(struct usb_xpad *xpad) { }
  #endif
@@ -971,42 +961,6 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
  
  	if (xpad->xtype == XTYPE_XBOX360W) {
  		/*
-		 * Setup the message to set the LEDs on the
-		 * controller when it shows up
-		 */
-		xpad->bulk_out = usb_alloc_urb(0, GFP_KERNEL);
-		if (!xpad->bulk_out) {
-			error = -ENOMEM;
-			goto fail7;
-		}
-
-		xpad->bdata = kzalloc(XPAD_PKT_LEN, GFP_KERNEL);
-		if (!xpad->bdata) {
-			error = -ENOMEM;
-			goto fail8;
-		}
-
-		xpad->bdata[2] = 0x08;
-		switch (intf->cur_altsetting->desc.bInterfaceNumber) {
-		case 0:
-			xpad->bdata[3] = 0x42;
-			break;
-		case 2:
-			xpad->bdata[3] = 0x43;
-			break;
-		case 4:
-			xpad->bdata[3] = 0x44;
-			break;
-		case 6:
-			xpad->bdata[3] = 0x45;
-		}
-
-		ep_irq_in = &intf->cur_altsetting->endpoint[1].desc;
-		usb_fill_bulk_urb(xpad->bulk_out, udev,
-				usb_sndbulkpipe(udev, ep_irq_in->bEndpointAddress),
-				xpad->bdata, XPAD_PKT_LEN, xpad_bulk_out, xpad);
-
-		/*
  		 * Submit the int URB immediately rather than waiting for open
  		 * because we get status messages from the device whether
  		 * or not any controllers are attached.  In fact, it's
@@ -1016,13 +970,11 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
  		xpad->irq_in->dev = xpad->udev;
  		error = usb_submit_urb(xpad->irq_in, GFP_KERNEL);
  		if (error)
-			goto fail9;
+			goto fail7;
  	}
  
  	return 0;
  
- fail9:	kfree(xpad->bdata);
- fail8:	usb_free_urb(xpad->bulk_out);
   fail7:	input_unregister_device(input_dev);
  	input_dev = NULL;
   fail6:	xpad_led_disconnect(xpad);
@@ -1046,8 +998,6 @@ static void xpad_disconnect(struct usb_interface *intf)
  	xpad_deinit_output(xpad);
  
  	if (xpad->xtype == XTYPE_XBOX360W) {
-		usb_kill_urb(xpad->bulk_out);
-		usb_free_urb(xpad->bulk_out);
  		usb_kill_urb(xpad->irq_in);
  	}
  
@@ -1055,7 +1005,6 @@ static void xpad_disconnect(struct usb_interface *intf)
  	usb_free_coherent(xpad->udev, XPAD_PKT_LEN,
  			xpad->idata, xpad->idata_dma);
  
-	kfree(xpad->bdata);
  	kfree(xpad);
  
  	usb_set_intfdata(intf, NULL);



^ permalink raw reply related

* [PATCH] Input: add i2c/smbus driver for elan touchpad
From: Duson Lin @ 2013-11-22  5:56 UTC (permalink / raw)
  To: linux-input, linux-kernel, dmitry.torokhov, agnescheng, phoenix; +Cc: dusonlin

This driver adds support for elan i2c/smbus touchpad found on some laptops PC
---
 drivers/input/mouse/Kconfig    |   10 +
 drivers/input/mouse/Makefile   |    1 +
 drivers/input/mouse/elan_i2c.c | 1846 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1857 insertions(+)
 create mode 100644 drivers/input/mouse/elan_i2c.c

diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index effa9c5..8ad4b38 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -215,6 +215,16 @@ config MOUSE_CYAPA
 	  To compile this driver as a module, choose M here: the module will be
 	  called cyapa.
 
+config MOUSE_ELAN_I2C
+	tristate "ELAN I2C Touchpad support"
+	depends on I2C
+	help
+	  This driver adds support for Elan I2C Trackpads.
+	  Say y here if you have a ELAN I2C Touchpad.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called elan_i2c.
+
 config MOUSE_INPORT
 	tristate "InPort/MS/ATIXL busmouse"
 	depends on ISA
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index c25efdb..24a12a6 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_MOUSE_APPLETOUCH)		+= appletouch.o
 obj-$(CONFIG_MOUSE_ATARI)		+= atarimouse.o
 obj-$(CONFIG_MOUSE_BCM5974)		+= bcm5974.o
 obj-$(CONFIG_MOUSE_CYAPA)		+= cyapa.o
+obj-$(CONFIG_MOUSE_ELAN_I2C)		+= elan_i2c.o
 obj-$(CONFIG_MOUSE_GPIO)		+= gpio_mouse.o
 obj-$(CONFIG_MOUSE_INPORT)		+= inport.o
 obj-$(CONFIG_MOUSE_LOGIBM)		+= logibm.o
diff --git a/drivers/input/mouse/elan_i2c.c b/drivers/input/mouse/elan_i2c.c
new file mode 100644
index 0000000..9892ee1
--- /dev/null
+++ b/drivers/input/mouse/elan_i2c.c
@@ -0,0 +1,1846 @@
+/*
+ * Elan I2C/SMBus Touchpad driver
+ *
+ * Copyright (c) 2013 ELAN Microelectronics Corp.
+ *
+ * Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
+ * Version: 1.4.6
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/cdev.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/input.h>
+#include <linux/uaccess.h>
+#include <linux/jiffies.h>
+
+#define DRIVER_NAME		"elan_i2c"
+#define ELAN_DRIVER_VERSION	"1.4.6"
+#define ETP_PRESSURE_OFFSET	25
+#define ETP_MAX_PRESSURE	255
+#define ETP_FWIDTH_REDUCE	90
+#define ETP_FINGER_WIDTH	15
+
+#define ELAN_ADAPTER_FUNC_NONE   0
+#define ELAN_ADAPTER_FUNC_I2C    1
+#define ELAN_ADAPTER_FUNC_SMBUS  2
+#define ELAN_ADAPTER_FUNC_BOTH   3
+
+/* Length of Elan touchpad information */
+#define ETP_INF_LENGTH		2
+#define ETP_MAX_FINGERS		5
+#define ETP_FINGER_DATA_LEN	5
+#define ETP_REPORT_ID		0x5D
+#define ETP_MAX_REPORT_LEN	34
+#define ETP_ENABLE_ABS		0x0001
+#define ETP_ENABLE_CALIBRATE	0x0002
+#define ETP_DISABLE_CALIBRATE	0x0000
+
+/* Elan smbus command */
+#define ETP_SMBUS_IAP_CMD		0x00
+#define ETP_SMBUS_ENABLE_TP		0x20
+#define ETP_SMBUS_DISABLE_TP		0x21
+#define ETP_SMBUS_IAP_PASSWORD_WRITE	0x29
+#define ETP_SMBUS_IAP_PASSWORD_READ	0x80
+#define ETP_SMBUS_WRITE_FW_BLOCK	0x2A
+#define ETP_SMBUS_IAP_RESET_CMD		0x2B
+#define ETP_SMBUS_RANGE_CMD		0xA0
+#define ETP_SMBUS_FW_VERSION_CMD	0xA1
+#define ETP_SMBUS_XY_TRACENUM_CMD	0xA2
+#define ETP_SMBUS_SM_VERSION_CMD	0xA3
+#define ETP_SMBUS_UNIQUEID_CMD		0xA3
+#define ETP_SMBUS_RESOLUTION_CMD	0xA4
+#define ETP_SMBUS_HELLOPACKET_CMD	0xA7
+#define ETP_SMBUS_PACKET_QUERY		0xA8
+#define ETP_SMBUS_IAP_VERSION_CMD	0xAC
+#define ETP_SMBUS_IAP_CTRL_CMD		0xAD
+#define ETP_SMBUS_IAP_CHECKSUM_CMD	0xAE
+#define ETP_SMBUS_FW_CHECKSUM_CMD	0xAF
+#define ETP_SMBUS_MAX_BASELINE_CMD	0xC3
+#define ETP_SMBUS_MIN_BASELINE_CMD	0xC4
+#define ETP_SMBUS_CALIBRATE_QUERY	0xC5
+#define ETP_SMBUS_REPORT_LEN		32
+#define ETP_SMBUS_FINGER_DATA_OFFSET	2
+#define ETP_SMBUS_HELLOPACKET_LEN	5
+#define ETP_SMBUS_IAP_PASSWORD		0x1234
+#define ETP_SMBUS_IAP_MODE_ON		(1<<6)
+
+/* Elan i2c command */
+#define ETP_I2C_RESET			0x0100
+#define ETP_I2C_WAKE_UP			0x0800
+#define ETP_I2C_SLEEP			0x0801
+#define ETP_I2C_DESC_CMD		0x0001
+#define ETP_I2C_REPORT_DESC_CMD		0x0002
+#define ETP_I2C_STAND_CMD		0x0005
+#define ETP_I2C_UNIQUEID_CMD		0x0101
+#define ETP_I2C_FW_VERSION_CMD		0x0102
+#define ETP_I2C_SM_VERSION_CMD		0x0103
+#define ETP_I2C_XY_TRACENUM_CMD		0x0105
+#define ETP_I2C_MAX_X_AXIS_CMD		0x0106
+#define ETP_I2C_MAX_Y_AXIS_CMD		0x0107
+#define ETP_I2C_RESOLUTION_CMD		0x0108
+#define ETP_I2C_IAP_VERSION_CMD		0x0110
+#define ETP_I2C_SET_CMD			0x0300
+#define ETP_I2C_MAX_BASELINE_CMD	0x0306
+#define ETP_I2C_MIN_BASELINE_CMD	0x0307
+#define ETP_I2C_FW_CHECKSUM_CMD		0x030F
+#define ETP_I2C_IAP_CTRL_CMD		0x0310
+#define ETP_I2C_IAP_CMD			0x0311
+#define ETP_I2C_IAP_RESET_CMD		0x0314
+#define ETP_I2C_IAP_CHECKSUM_CMD	0x0315
+#define ETP_I2C_CALIBRATE_CMD		0x0316
+#define ETP_I2C_REPORT_LEN		34
+#define ETP_I2C_FINGER_DATA_OFFSET	4
+#define ETP_I2C_REPORT_ID_OFFSET	2
+#define ETP_I2C_DESC_LENGTH		30
+#define ETP_I2C_REPORT_DESC_LENGTH	158
+#define ETP_I2C_IAP_PASSWORD		0x1EA5
+#define ETP_I2C_IAP_RESET		0xF0F0
+#define ETP_I2C_MAIN_MODE_ON		(1<<9)
+#define ETP_I2C_IAP_REG_L		0x01
+#define ETP_I2C_IAP_REG_H		0x06
+
+/* IAP F/W updater */
+#define ETP_FW_NAME		"elan_i2c.bin"
+#define ETP_IAP_VERSION_ADDR	0x0082
+#define ETP_IAP_START_ADDR	0x0083
+#define ETP_FW_IAP_PAGE_ERR	(1<<5)
+#define ETP_FW_IAP_INTERFACE_ERR (1<<4)
+#define ETP_FW_PAGE_SIZE	64
+#define ETP_FW_PAGE_COUNT	768
+#define ETP_FW_SIZE		(ETP_FW_PAGE_SIZE * ETP_FW_PAGE_COUNT)
+enum {UNKNOWN_MODE, IAP_MODE, MAIN_MODE};
+
+struct dbfs_data {
+	bool	bfetch;
+	u8	buffer[ETP_MAX_REPORT_LEN];
+};
+
+/* The main device structure */
+struct elan_tp_data {
+	struct i2c_client	*client;
+	struct input_dev	*input;
+	unsigned int		max_x;
+	unsigned int		max_y;
+	unsigned int		width_x;
+	unsigned int		width_y;
+	unsigned int		irq;
+
+	/* fields required for IAP firmware updater */
+	u16			unique_id;
+	u16			fw_version;
+	u16			sm_version;
+	u16			iap_version;
+	bool			updated_fw;
+	u16			iap_start_addr;
+
+	/* irq wake is enabled */
+	bool			irq_wake;
+	bool			smbus;
+	bool			enable_detail_info;
+
+	/* fields required for debug fs */
+	struct mutex		dbfs_mutex;
+	struct dentry		*dbfs_root;
+	struct dbfs_data	dbfs_buffer;
+};
+
+u8 val[256];
+static int elan_i2c_read_cmd(struct i2c_client *client, u16 reg, u8 *val);
+static int elan_i2c_write_cmd(struct i2c_client *client, u16 reg, u16 cmd);
+static int elan_initialize(struct elan_tp_data *data);
+
+/*
+ **************************************************************
+ * debugfs interface
+ **************************************************************
+*/
+static int elan_dbfs_open(struct inode *inode, struct file *file)
+{
+	int retval;
+	struct elan_tp_data *data = inode->i_private;
+
+	if (!data)
+		return -ENODEV;
+
+	retval = mutex_lock_interruptible(&data->dbfs_mutex);
+	if (retval)
+		return retval;
+
+	if (!kobject_get(&data->client->dev.kobj)) {
+		retval = -ENODEV;
+		goto dbfs_out;
+	}
+
+	file->private_data = data;
+dbfs_out:
+	mutex_unlock(&data->dbfs_mutex);
+	return 0;
+}
+
+static int elan_dbfs_release(struct inode *inode, struct file *file)
+{
+	struct elan_tp_data *data = file->private_data;
+	int retval;
+	if (!data)
+		return -ENODEV;
+
+	retval = mutex_lock_interruptible(&data->dbfs_mutex);
+	if (retval)
+		return retval;
+	file->private_data = NULL;
+	kobject_put(&data->client->dev.kobj);
+	mutex_unlock(&data->dbfs_mutex);
+	return 0;
+}
+
+
+static ssize_t elan_dbfs_read(struct file *file,
+		char __user *buffer, size_t count, loff_t *ppos)
+{
+	struct elan_tp_data *data = file->private_data;
+	int retval;
+	if (!data)
+		return -ENODEV;
+
+	retval = mutex_lock_interruptible(&data->dbfs_mutex);
+	if (retval)
+		return -EFAULT;
+	if (data->dbfs_buffer.bfetch == false) {
+		if (!copy_to_user(buffer, data->dbfs_buffer.buffer, count)) {
+			data->dbfs_buffer.bfetch = true;
+			retval = count;
+		} else {
+			retval = -2;
+		}
+	} else {
+		retval = -4;
+	}
+	mutex_unlock(&data->dbfs_mutex);
+	return retval;
+}
+
+static ssize_t elan_dbfs_write(struct file *file,
+		const char __user *buffer, size_t count, loff_t *ppos)
+{
+	struct elan_tp_data *data = file->private_data;
+	int retval;
+	if (!data)
+		return -ENODEV;
+
+	retval = mutex_lock_interruptible(&data->dbfs_mutex);
+	if (retval)
+		return -EFAULT;
+	retval = count;
+	mutex_unlock(&data->dbfs_mutex);
+	return retval;
+}
+
+static long elan_dbfs_ioctrl(struct file *file,
+		unsigned int cmd, unsigned long arg)
+{
+	int retval = 0;
+	struct elan_tp_data *data = file->private_data;
+
+	retval = mutex_lock_interruptible(&data->dbfs_mutex);
+	if (retval)
+		return retval;
+	mutex_unlock(&data->dbfs_mutex);
+	return retval;
+}
+
+static const struct file_operations elan_debug_fops = {
+	.open = elan_dbfs_open,
+	.release = elan_dbfs_release,
+	.read = elan_dbfs_read,
+	.write = elan_dbfs_write,
+	.unlocked_ioctl = elan_dbfs_ioctrl
+};
+
+static int elan_dbfs_init(struct elan_tp_data *data)
+{
+	/* Create a global debugfs root for all elan devices */
+	/* sys/kernel/debug/elan */
+	data->dbfs_root = debugfs_create_dir("elan", NULL);
+	if (!data->dbfs_root) {
+		dev_err(&data->client->dev, "cannot create dbfs_root.\n");
+		return -ENODEV;
+	}
+	mutex_init(&data->dbfs_mutex);
+
+	debugfs_create_file(DRIVER_NAME, 0777,
+				data->dbfs_root, data, &elan_debug_fops);
+	data->dbfs_buffer.bfetch = false;
+	return 0;
+}
+
+/**********************************************************
+ * IAP firmware updater related routines                  *
+ **********************************************************
+*/
+
+static int elan_iap_getmode(struct elan_tp_data *data)
+{
+	u16 constant;
+	int retval;
+	struct i2c_client *client = data->client;
+
+	if (data->smbus) {
+		retval = i2c_smbus_read_block_data(client,
+					ETP_SMBUS_IAP_CTRL_CMD, val);
+		if (retval < 0) {
+			dev_err(&client->dev, "read iap ctrol fail.\n");
+			return UNKNOWN_MODE;
+		}
+		constant = be16_to_cpup((__be16 *)val);
+		dev_dbg(&client->dev, "smbus iap control reg: 0x%04x.\n",
+								constant);
+		if ((constant & ETP_SMBUS_IAP_MODE_ON) == 0x00)
+			return MAIN_MODE;
+	} else {
+		retval = elan_i2c_read_cmd(client, ETP_I2C_IAP_CTRL_CMD, val);
+		if (retval < 0) {
+			dev_err(&client->dev, "read iap ctrol fail.\n");
+			return UNKNOWN_MODE;
+		}
+		constant = le16_to_cpup((__le16 *)val);
+		dev_dbg(&client->dev, "i2c iap control reg: 0x%04x.\n",
+								constant);
+		if (constant & ETP_I2C_MAIN_MODE_ON)
+			return MAIN_MODE;
+	}
+
+	return IAP_MODE;
+}
+
+static int elan_iap_checksum(struct elan_tp_data *data)
+{
+	int retval = 0;
+	u16 checksum = -1;
+	struct i2c_client *client = data->client;
+
+	if (data->smbus) {
+		retval = i2c_smbus_read_block_data(client,
+					ETP_SMBUS_IAP_CHECKSUM_CMD, val);
+		if (retval < 0) {
+			dev_err(&client->dev, "Read checksum fail, %d\n",
+								retval);
+			return -1;
+		}
+		checksum = be16_to_cpup((__be16 *)val);
+	} else {
+		retval = elan_i2c_read_cmd(client,
+					ETP_I2C_IAP_CHECKSUM_CMD, val);
+		if (retval < 0) {
+			dev_err(&client->dev, "Read checksum fail, %d\n",
+								retval);
+			return -1;
+		}
+		checksum = le16_to_cpup((__le16 *)val);
+	}
+	return checksum;
+}
+
+static bool elan_iap_reset(struct elan_tp_data *data)
+{
+	int retval = 0;
+	struct i2c_client *client = data->client;
+
+	if (data->smbus)
+		retval = i2c_smbus_write_byte(client,
+						ETP_SMBUS_IAP_RESET_CMD);
+	else
+		retval = elan_i2c_write_cmd(client, ETP_I2C_IAP_RESET_CMD,
+						ETP_I2C_IAP_RESET);
+	if (retval < 0) {
+		dev_err(&client->dev, "cannot reset IC, %d\n", retval);
+		return false;
+	}
+	return true;
+}
+
+static bool elan_iap_setflashkey(struct elan_tp_data *data)
+{
+	int retval = 0;
+	struct i2c_client *client = data->client;
+	u8 smbus_cmd[4] = {0x00, 0x0B, 0x00, 0x5A};
+
+	if (data->smbus)
+		retval = i2c_smbus_write_block_data(client,
+				ETP_SMBUS_IAP_CMD, 4, smbus_cmd);
+	else
+		retval = elan_i2c_write_cmd(client, ETP_I2C_IAP_CMD,
+					ETP_I2C_IAP_PASSWORD);
+	if (retval < 0) {
+		dev_err(&client->dev, "cannot set flash key, %d\n", retval);
+		return false;
+	}
+
+	return true;
+}
+
+static int elan_check_fw(struct elan_tp_data *data,
+				const struct firmware *fw)
+{
+	struct device *dev = &data->client->dev;
+
+	/* Firmware must match exact PAGE_NUM * PAGE_SIZE bytes */
+	if (fw->size != ETP_FW_SIZE) {
+		dev_err(dev, "invalid firmware size = %zu, expected %d.\n",
+					fw->size, ETP_FW_SIZE);
+		return -EBADF;
+	}
+
+	/* Get IAP Start Address*/
+	memcpy(val, &fw->data[ETP_IAP_START_ADDR * 2], 2);
+	data->iap_start_addr = le16_to_cpup((__le16 *)val);
+	return 0;
+}
+
+
+static int elan_smbus_prepare_fw_update(struct elan_tp_data *data)
+{
+	struct i2c_client *client = data->client;
+	struct device *dev = &data->client->dev;
+	u16 password;
+	u8 cmd[4] = {0x0F, 0x78, 0x00, 0x06};
+
+	/* Get FW in which mode	(IAP_MODE/MAIN_MODE)  */
+	int mode = elan_iap_getmode(data);
+	if (mode == UNKNOWN_MODE)
+		return -1;
+
+	if (mode == MAIN_MODE) {
+
+		/* set flash key*/
+		if (elan_iap_setflashkey(data) == false) {
+			dev_err(dev, "cannot set flash key\n");
+			return -1;
+		}
+
+		/* write iap password */
+		if (i2c_smbus_write_byte(client,
+				ETP_SMBUS_IAP_PASSWORD_WRITE) < 0) {
+			dev_err(dev, "cannot write iap password\n");
+			return -1;
+		}
+
+		if (i2c_smbus_write_block_data(client,
+				ETP_SMBUS_IAP_CMD, 4, cmd) < 0) {
+			dev_err(dev, "cannot write cmd\n");
+			return -1;
+		}
+
+		/* read password to check we enabled successfully. */
+		if (i2c_smbus_read_block_data(client,
+				ETP_SMBUS_IAP_PASSWORD_READ, val) < 0) {
+			dev_err(dev, "cannot get iap password\n");
+			return -1;
+		}
+		password = be16_to_cpup((__be16 *)val);
+
+		if (password != ETP_SMBUS_IAP_PASSWORD) {
+			dev_err(dev, "wrong iap password = 0x%X\n", password);
+			return -1;
+		}
+		/* wait 30ms, from MAIN_MODE change to IAP_MODE*/
+		msleep(30);
+	}
+
+	/* set flash key*/
+	if (elan_iap_setflashkey(data) == false) {
+		dev_err(dev, "cannot set flash key\n");
+		return -1;
+	}
+
+	/* Reset IC */
+	if (elan_iap_reset(data) == false) {
+		dev_err(dev, "iap reset fail.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int elan_i2c_prepare_fw_update(struct elan_tp_data *data)
+{
+	struct i2c_client *client = data->client;
+	struct device *dev = &data->client->dev;
+	u16 password;
+
+	/* Get FW in which mode	(IAP_MODE/MAIN_MODE)  */
+	int mode = elan_iap_getmode(data);
+	if (mode == UNKNOWN_MODE)
+		return -1;
+
+	if (mode == IAP_MODE) {
+		/* Reset IC */
+		if (elan_iap_reset(data) == false)
+			return -1;
+		msleep(30);
+	}
+
+	/* set flash key*/
+	if (elan_iap_setflashkey(data) == false) {
+		dev_err(dev, "cannot set flash key\n");
+		return -1;
+	}
+
+	/* Wait for F/W IAP initialization */
+	if (mode == MAIN_MODE)
+		msleep(100);
+	else
+		msleep(30);
+
+	/* check is in iap mode or not*/
+	if (elan_iap_getmode(data) == MAIN_MODE) {
+		dev_err(dev, "status wrong.\n");
+		return -1;
+	}
+
+	/* set flash key again */
+	if (elan_iap_setflashkey(data) == false) {
+		dev_err(dev, "cannot set flash key\n");
+		return -1;
+	}
+
+	/* Wait for F/W IAP initialization */
+	if (mode == MAIN_MODE)
+		msleep(100);
+	else
+		msleep(30);
+
+	/* read back to check we actually enabled successfully. */
+	if (elan_i2c_read_cmd(client, ETP_I2C_IAP_CMD, val) < 0) {
+		dev_err(dev, "cannot get iap register\n");
+		return -1;
+	}
+	password = le16_to_cpup((__le16 *)val);
+
+	if (password != ETP_I2C_IAP_PASSWORD) {
+		dev_err(dev, "wrong iap password = 0x%X\n", password);
+		return -1;
+	}
+	return 0;
+}
+
+static bool elan_iap_page_write_ok(struct elan_tp_data *data)
+{
+	u16 constant;
+	int retval = 0;
+	struct i2c_client *client = data->client;
+
+
+	if (data->smbus) {
+		retval = i2c_smbus_read_block_data(client,
+					ETP_SMBUS_IAP_CTRL_CMD, val);
+		if (retval < 0)
+			return false;
+		constant = be16_to_cpup((__be16 *)val);
+	} else {
+		retval = elan_i2c_read_cmd(client,
+					ETP_I2C_IAP_CTRL_CMD, val);
+		if (retval < 0)
+			return false;
+		constant = le16_to_cpup((__le16 *)val);
+	}
+
+	if (constant & ETP_FW_IAP_PAGE_ERR)
+		return false;
+
+	if (constant & ETP_FW_IAP_INTERFACE_ERR)
+		return false;
+	return true;
+}
+
+static int elan_smbus_write_fw_block(struct elan_tp_data *data,
+				const u8 *page, u16 checksum, int idx)
+{
+	struct device *dev = &data->client->dev;
+	int half_page_size = ETP_FW_PAGE_SIZE / 2;
+	int repeat = 3;
+
+	do {
+		/* due to smbus can write 32 bytes one time,
+		so, we must write data 2 times.
+		*/
+		i2c_smbus_write_block_data(data->client,
+					ETP_SMBUS_WRITE_FW_BLOCK,
+					half_page_size,
+					page);
+		i2c_smbus_write_block_data(data->client,
+					ETP_SMBUS_WRITE_FW_BLOCK,
+					half_page_size,
+					(page + half_page_size));
+		/* Wait for F/W to update one page ROM data. */
+		usleep_range(8000, 10000);
+		if (elan_iap_page_write_ok(data))
+			break;
+		dev_info(dev, "IAP retry this page! [%d]\n", idx);
+		repeat--;
+	} while (repeat == 0);
+
+	if (repeat > 0)
+		return 0;
+	return -1;
+
+}
+
+static int elan_i2c_write_fw_block(struct elan_tp_data *data,
+				const u8 *page, u16 checksum, int idx)
+{
+	struct device *dev = &data->client->dev;
+	int ret;
+	int repeat = 3;
+	u8 page_store[ETP_FW_PAGE_SIZE + 4];
+
+	page_store[0] = ETP_I2C_IAP_REG_L;
+	page_store[1] = ETP_I2C_IAP_REG_H;
+	memcpy(&page_store[2], page, ETP_FW_PAGE_SIZE);
+
+	/* recode checksum at last two bytes */
+	page_store[ETP_FW_PAGE_SIZE+2] = (u8)(checksum & 0xFF);
+	page_store[ETP_FW_PAGE_SIZE+3] = (u8)((checksum >> 8)&0xFF);
+
+	do {
+		ret = i2c_master_send(data->client, page_store,
+						ETP_FW_PAGE_SIZE + 4);
+
+		/* Wait for F/W to update one page ROM data. */
+		msleep(20);
+
+		if (ret == (ETP_FW_PAGE_SIZE + 4)) {
+			if (elan_iap_page_write_ok(data))
+				break;
+		}
+		dev_dbg(dev, "IAP retry this page! [%d]\n", idx);
+		repeat--;
+	} while (repeat == 0);
+
+	if (repeat > 0)
+		return 0;
+	return -1;
+}
+
+static int elan_write_fw_block(struct elan_tp_data *data,
+				const u8 *page, u16 checksum, int idx)
+{
+	int ret;
+	if (data->smbus)
+		ret = elan_smbus_write_fw_block(data, page, checksum, idx);
+	else
+		ret = elan_i2c_write_fw_block(data, page, checksum, idx);
+	return ret;
+}
+
+static int elan_prepare_fw_update(struct elan_tp_data *data)
+{
+	int ret = 0;
+	if (data->smbus)
+		ret = elan_smbus_prepare_fw_update(data);
+	else
+		ret = elan_i2c_prepare_fw_update(data);
+	return ret;
+}
+
+static int elan_firmware(struct elan_tp_data *data)
+{
+	struct device *dev = &data->client->dev;
+	const struct firmware *fw;
+	const char *fw_name = ETP_FW_NAME;
+	int i, j, ret;
+	u16 boot_page_count;
+	u16 sw_checksum, fw_checksum;
+	data->updated_fw = true;
+
+	dev_info(dev, "Start firmware update....\n");
+
+	ret = request_firmware(&fw, ETP_FW_NAME, dev);
+	if (ret) {
+		dev_err(dev, "cannot load firmware from %s, %d\n",
+							fw_name, ret);
+		goto done;
+	}
+	/* check fw data match current iap version */
+	ret = elan_check_fw(data, fw);
+	if (ret) {
+		dev_err(dev, "Invalid Elan firmware from %s, %d\n",
+							fw_name, ret);
+		goto done;
+	}
+	/* setup IAP status */
+	ret = elan_prepare_fw_update(data);
+	if (ret)
+		goto done;
+	sw_checksum = 0;
+	fw_checksum = 0;
+	boot_page_count = (data->iap_start_addr * 2) / ETP_FW_PAGE_SIZE;
+	for (i = boot_page_count; i < ETP_FW_PAGE_COUNT; i++) {
+		u16 checksum = 0;
+		const u8 *page = &fw->data[i * ETP_FW_PAGE_SIZE];
+
+		for (j = 0; j < ETP_FW_PAGE_SIZE; j += 2)
+			checksum += ((page[j + 1] << 8) | page[j]);
+
+		ret = elan_write_fw_block(data, page, checksum, i);
+		if (ret) {
+			dev_err(dev, "write page %d fail\n", i);
+			goto done;
+		}
+		sw_checksum += checksum;
+	}
+
+	/* Wait WDT reset and power on reset */
+	msleep(600);
+
+	/* check checksum */
+	fw_checksum = elan_iap_checksum(data);
+	if (sw_checksum != fw_checksum) {
+		dev_err(dev, "checksum diff sw=[%04X], fw=[%04X]\n",
+					sw_checksum, fw_checksum);
+		ret = -1;
+		goto done;
+	}
+	ret = 0;
+done:
+	if (ret != 0) {
+		elan_iap_reset(data);
+		data->updated_fw = false;
+	} else {
+		if (data->smbus) {
+			data->updated_fw = false;
+			elan_initialize(data);
+		}
+	}
+	release_firmware(fw);
+	return ret;
+}
+
+/******************************************************************
+* Elan smbus interface
+*******************************************************************
+*/
+static int elan_smbus_initialize(struct i2c_client *client)
+{
+	u8 check[ETP_SMBUS_HELLOPACKET_LEN] = {0x55, 0x55, 0x55, 0x55, 0x55};
+	u8 values[ETP_SMBUS_HELLOPACKET_LEN] = {0, 0, 0, 0, 0};
+	int ret;
+
+	/* Get hello packet */
+	ret = i2c_smbus_read_block_data(client,
+				ETP_SMBUS_HELLOPACKET_CMD, values);
+	if (ret != ETP_SMBUS_HELLOPACKET_LEN) {
+		dev_err(&client->dev, "hello packet length fail\n");
+		return -1;
+	}
+
+	/* compare hello packet */
+	if (memcmp(values, check, ETP_SMBUS_HELLOPACKET_LEN)) {
+		dev_err(&client->dev, "hello packet fail [%x %x %x %x %x]\n",
+		values[0], values[1], values[2], values[3], values[4]);
+		return -1;
+	}
+
+	/* enable tp */
+	ret = i2c_smbus_write_byte(client, ETP_SMBUS_ENABLE_TP);
+	return ret;
+}
+
+static int elan_smbus_enable_calibrate(struct i2c_client *client)
+{
+	u8 cmd[4] = {0x00, 0x07, 0x00, ETP_ENABLE_ABS|ETP_ENABLE_CALIBRATE};
+
+	return i2c_smbus_write_block_data(client,
+				ETP_SMBUS_IAP_CMD, 4, cmd);
+}
+
+static int elan_smbus_disable_calibrate(struct i2c_client *client)
+{
+	u8 cmd[4] = {0x00, 0x07, 0x00, ETP_ENABLE_ABS|ETP_DISABLE_CALIBRATE};
+
+	return i2c_smbus_write_block_data(client,
+				ETP_SMBUS_IAP_CMD, 4, cmd);
+}
+
+static int elan_smbus_enable_absolute_mode(struct i2c_client *client)
+{
+	u8 cmd[4] = {0x00, 0x07, 0x00, ETP_ENABLE_ABS};
+
+	return i2c_smbus_write_block_data(client, ETP_SMBUS_IAP_CMD, 4, cmd);
+}
+
+/*****************************************************************
+* Elan i2c interface
+******************************************************************
+*/
+static int elan_i2c_read_block(struct i2c_client *client,
+				 u16 reg, u8 *val, u16 len)
+{
+	struct i2c_msg msgs[2];
+	u8 buf[2];
+	int ret;
+
+	buf[0] = reg & 0xff;
+	buf[1] = (reg >> 8) & 0xff;
+
+	msgs[0].addr = client->addr;
+	msgs[0].flags = client->flags & I2C_M_TEN;
+	msgs[0].len = 2;
+	msgs[0].buf = buf;
+
+	msgs[1].addr = client->addr;
+	msgs[1].flags = client->flags & I2C_M_TEN;
+	msgs[1].flags |= I2C_M_RD;
+	msgs[1].len = len;
+	msgs[1].buf = val;
+
+	ret = i2c_transfer(client->adapter, msgs, 2);
+	return ret != 2 ? -EIO : 0;
+}
+
+static int elan_i2c_read_cmd(struct i2c_client *client, u16 reg, u8 *val)
+{
+	int retval;
+
+	retval = elan_i2c_read_block(client, reg, val, ETP_INF_LENGTH);
+	if (retval < 0) {
+		dev_err(&client->dev, "reading cmd (0x%04x) fail.\n", reg);
+		return retval;
+	}
+	return 0;
+}
+
+static int elan_i2c_write_cmd(struct i2c_client *client, u16 reg, u16 cmd)
+{
+	struct i2c_msg msg;
+	u8 buf[4];
+	int ret;
+
+	buf[0] = reg & 0xff;
+	buf[1] = (reg >> 8) & 0xff;
+	buf[2] = cmd & 0xff;
+	buf[3] = (cmd >> 8) & 0xff;
+
+	msg.addr = client->addr;
+	msg.flags = client->flags & I2C_M_TEN;
+	msg.len = 4;
+	msg.buf = buf;
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	return ret != 1 ? -EIO : 0;
+}
+
+static int elan_i2c_reset(struct i2c_client *client)
+{
+	return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD,
+						ETP_I2C_RESET);
+}
+
+static int elan_i2c_wake_up(struct i2c_client *client)
+{
+	return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD,
+						ETP_I2C_WAKE_UP);
+}
+
+static int elan_i2c_sleep(struct i2c_client *client)
+{
+	return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD,
+						ETP_I2C_SLEEP);
+}
+
+static int elan_i2c_enable_absolute_mode(struct i2c_client *client)
+{
+	return elan_i2c_write_cmd(client, ETP_I2C_SET_CMD,
+						ETP_ENABLE_ABS);
+}
+
+static int elan_i2c_enable_calibrate(struct i2c_client *client)
+{
+	return elan_i2c_write_cmd(client, ETP_I2C_SET_CMD,
+			ETP_ENABLE_ABS|ETP_ENABLE_CALIBRATE);
+}
+
+static int elan_i2c_disable_calibrate(struct i2c_client *client)
+{
+	return elan_i2c_write_cmd(client, ETP_I2C_SET_CMD,
+			ETP_ENABLE_ABS|ETP_DISABLE_CALIBRATE);
+}
+
+static int elan_i2c_get_desc(struct i2c_client *client, u8 *val)
+{
+	return elan_i2c_read_block(client, ETP_I2C_DESC_CMD, val,
+					ETP_I2C_DESC_LENGTH);
+}
+
+static int elan_i2c_get_report_desc(struct i2c_client *client, u8 *val)
+{
+	return elan_i2c_read_block(client, ETP_I2C_REPORT_DESC_CMD,
+				 val, ETP_I2C_REPORT_DESC_LENGTH);
+}
+
+static int elan_i2c_initialize(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	int rc;
+
+	rc = elan_i2c_reset(client);
+	if (rc < 0) {
+		dev_err(dev, "device reset failed.\n");
+		return -1;
+	}
+
+	/* wait for get reset return flag */
+	msleep(100);
+	/* get reset return flag 0000 */
+	rc = i2c_master_recv(client, val, ETP_INF_LENGTH);
+	if (rc < 0) {
+		dev_err(dev, "get device reset return value failed.\n");
+		return -1;
+	}
+
+	rc = elan_i2c_get_desc(client, val);
+	if (rc < 0) {
+		dev_err(dev, "cannot get device descriptor.\n");
+		return -1;
+	}
+
+	rc = elan_i2c_get_report_desc(client, val);
+	if (rc < 0) {
+		dev_err(dev, "fetching report descriptor failed.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/**************************************************************************
+* Genernal functions
+***************************************************************************
+*/
+
+/*
+ * (value from firmware) * 10 + 790 = dpi
+ * we also have to convert dpi to dots/mm (*10/254 to avoid floating point)
+ */
+static unsigned int elan_convert_res(char val)
+{
+	int res;
+	if (val & 0x80) {
+		val = ~val + 1;
+		res = (790 - val * 10) * 10 / 254;
+	} else
+		res = (val * 10 + 790) * 10 / 254;
+	return res;
+}
+
+static int elan_get_iap_version(struct elan_tp_data *data)
+{
+	int ret;
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+					ETP_SMBUS_IAP_VERSION_CMD, val);
+		ret = val[2];
+	} else {
+		elan_i2c_read_cmd(data->client,
+				ETP_I2C_IAP_VERSION_CMD, val);
+		ret = val[0];
+	}
+	return ret;
+}
+
+static int elan_get_x_max(struct elan_tp_data *data)
+{
+	int ret;
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+					ETP_SMBUS_RANGE_CMD, val);
+		ret = (0x0f & val[0]) << 8 | val[1];
+	} else {
+		elan_i2c_read_cmd(data->client,
+					ETP_I2C_MAX_X_AXIS_CMD, val);
+		ret = (0x0f & val[1]) << 8 | val[0];
+	}
+	return ret;
+}
+
+static int elan_get_y_max(struct elan_tp_data *data)
+{
+	int ret;
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+					ETP_SMBUS_RANGE_CMD, val);
+		ret = (0xf0 & val[0]) << 4 | val[2];
+	} else {
+		elan_i2c_read_cmd(data->client,
+					ETP_I2C_MAX_Y_AXIS_CMD, val);
+		ret = (0x0f & val[1]) << 8 | val[0];
+	}
+	return ret;
+}
+
+static int elan_get_x_tracenum(struct elan_tp_data *data)
+{
+	int ret;
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+				ETP_SMBUS_XY_TRACENUM_CMD, val);
+		ret = (val[1] - 1);
+	} else {
+		elan_i2c_read_cmd(data->client,
+				ETP_I2C_XY_TRACENUM_CMD, val);
+		ret = (val[0] - 1);
+	}
+	return ret;
+}
+
+static int elan_get_y_tracenum(struct elan_tp_data *data)
+{
+	int ret;
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+				ETP_SMBUS_XY_TRACENUM_CMD, val);
+		ret = (val[2] - 1);
+	} else {
+		ret = elan_i2c_read_cmd(data->client,
+				ETP_I2C_XY_TRACENUM_CMD, val);
+		ret = (val[1] - 1);
+	}
+	return ret;
+}
+
+static int elan_get_fw_version(struct elan_tp_data *data)
+{
+	int ret;
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+				ETP_SMBUS_FW_VERSION_CMD, val);
+		ret = val[2];
+	} else {
+		elan_i2c_read_cmd(data->client,
+				ETP_I2C_FW_VERSION_CMD, val);
+		ret = val[0];
+	}
+	return ret;
+}
+
+static int elan_get_sm_version(struct elan_tp_data *data)
+{
+	int ret;
+	if (data->smbus)
+		i2c_smbus_read_block_data(data->client,
+				ETP_SMBUS_SM_VERSION_CMD, val);
+	else
+		elan_i2c_read_block(data->client,
+				ETP_I2C_SM_VERSION_CMD, val, 1);
+	ret = val[0];
+	return ret;
+}
+
+static int elan_get_unique_id(struct elan_tp_data *data)
+{
+	int ret;
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+				ETP_SMBUS_UNIQUEID_CMD, val);
+		ret = val[1];
+	} else {
+		elan_i2c_read_cmd(data->client,
+				ETP_I2C_UNIQUEID_CMD, val);
+		ret = val[0];
+	}
+	return ret;
+}
+
+static int elan_get_x_resolution(struct elan_tp_data *data)
+{
+	int ret;
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+				ETP_SMBUS_RESOLUTION_CMD, val);
+		ret = elan_convert_res(val[1] & 0x0F);
+	} else {
+		elan_i2c_read_cmd(data->client,
+				ETP_I2C_RESOLUTION_CMD, val);
+		ret = elan_convert_res(val[0]);
+	}
+	return ret;
+}
+
+static int elan_get_y_resolution(struct elan_tp_data *data)
+{
+	int ret;
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+				ETP_SMBUS_RESOLUTION_CMD, val);
+		ret = elan_convert_res((val[1] & 0xF0) >> 4);
+	} else {
+		elan_i2c_read_cmd(data->client,
+				ETP_I2C_RESOLUTION_CMD, val);
+		ret = elan_convert_res(val[1]);
+	}
+	return ret;
+}
+
+static int elan_get_fw_checksum(struct elan_tp_data *data)
+{
+	int ret;
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+				ETP_SMBUS_FW_CHECKSUM_CMD, val);
+		ret = be16_to_cpup((__be16 *)val);
+	} else {
+		elan_i2c_read_cmd(data->client,
+				ETP_I2C_FW_CHECKSUM_CMD, val);
+		ret = le16_to_cpup((__le16 *)val);
+	}
+	return ret;
+}
+
+static int elan_get_max_baseline(struct elan_tp_data *data)
+{
+	int ret;
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+				ETP_SMBUS_MAX_BASELINE_CMD, val);
+		ret = be16_to_cpup((__be16 *)val);
+	} else {
+		elan_i2c_read_cmd(data->client,
+				ETP_I2C_MAX_BASELINE_CMD, val);
+		ret = le16_to_cpup((__le16 *)val);
+	}
+	return ret;
+}
+
+static int elan_get_min_baseline(struct elan_tp_data *data)
+{
+	int ret;
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+				ETP_SMBUS_MIN_BASELINE_CMD, val);
+		ret = be16_to_cpup((__be16 *)val);
+	} else {
+		elan_i2c_read_cmd(data->client,
+			ETP_I2C_MIN_BASELINE_CMD, val);
+		ret = le16_to_cpup((__le16 *)val);
+	}
+	return ret;
+}
+
+static int elan_enable_calibrate(struct elan_tp_data *data)
+{
+	int ret;
+	if (data->smbus)
+		ret = elan_smbus_enable_calibrate(data->client);
+	else
+		ret = elan_i2c_enable_calibrate(data->client);
+	return ret;
+}
+
+static int elan_disable_calibrate(struct elan_tp_data *data)
+{
+	int ret;
+	if (data->smbus)
+		ret = elan_smbus_disable_calibrate(data->client);
+	else
+		ret = elan_i2c_disable_calibrate(data->client);
+	return ret;
+}
+
+static int elan_initialize(struct elan_tp_data *data)
+{
+	int ret;
+	if (data->smbus) {
+		ret = elan_smbus_initialize(data->client);
+		if (ret < 0) {
+			dev_err(&data->client->dev,
+				"device initialize failed.\n");
+			goto err_initialize;
+		}
+
+		ret = elan_smbus_enable_absolute_mode(data->client);
+		if (ret < 0)
+			dev_err(&data->client->dev,
+				"cannot switch to absolute mode.\n");
+	} else {
+		ret = elan_i2c_initialize(data->client);
+		if (ret < 0) {
+			dev_err(&data->client->dev,
+				"device initialize failed.\n");
+			goto err_initialize;
+		}
+
+		ret = elan_i2c_enable_absolute_mode(data->client);
+		if (ret < 0) {
+			dev_err(&data->client->dev,
+				"cannot switch to absolute mode.\n");
+			goto err_initialize;
+		}
+
+		ret = elan_i2c_wake_up(data->client);
+		if (ret < 0)
+			dev_err(&data->client->dev,
+					"device wake up failed.\n");
+	}
+err_initialize:
+	return ret;
+}
+
+/********************************************************************
+ * below routines export interfaces to sysfs file system.
+ * so user can get firmware/driver/hardware information using cat command.
+ * e.g.: use below command to get firmware version
+ *      cat /sys/bus/i2c/drivers/elan_i2c/1-0015/firmware_version
+ *******************************************************************
+ */
+static ssize_t elan_sysfs_enable_detailinfo(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct elan_tp_data *data = dev_get_drvdata(dev);
+	data->enable_detail_info = true;
+	return sprintf(buf, "enable\n");
+}
+
+static ssize_t elan_sysfs_read_fw_checksum(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	unsigned int checksum = 0;
+	struct elan_tp_data *data = dev_get_drvdata(dev);
+	if (data->enable_detail_info == true) {
+		checksum = elan_get_fw_checksum(data);
+		data->enable_detail_info = false;
+	}
+	return sprintf(buf, "0x%04x\n", checksum);
+}
+
+static ssize_t elan_sysfs_read_unique_id(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct elan_tp_data *data = dev_get_drvdata(dev);
+	data->unique_id = elan_get_unique_id(data);
+	return sprintf(buf, "0x%04x\n", data->unique_id);
+}
+
+static ssize_t elan_sysfs_read_driver_ver(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	return sprintf(buf, "%s\n", ELAN_DRIVER_VERSION);
+}
+
+static ssize_t elan_sysfs_read_fw_ver(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	struct elan_tp_data *data = dev_get_drvdata(dev);
+	data->fw_version = elan_get_fw_version(data);
+	return sprintf(buf, "0x%04x\n", data->fw_version);
+}
+
+static ssize_t elan_sysfs_read_sm_ver(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	struct elan_tp_data *data = dev_get_drvdata(dev);
+	data->sm_version = elan_get_sm_version(data);
+	return sprintf(buf, "0x%04x\n", data->sm_version);
+}
+
+static ssize_t elan_sysfs_read_iap_ver(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct elan_tp_data *data = dev_get_drvdata(dev);
+	data->iap_version = elan_get_iap_version(data);
+	return sprintf(buf, "0x%04x\n", data->iap_version);
+}
+
+
+static ssize_t elan_sysfs_update_fw(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	struct elan_tp_data *data = dev_get_drvdata(dev);
+	int ret;
+	ret = elan_firmware(data);
+	if (ret)
+		dev_err(dev, "firmware update failed.\n");
+	else
+		dev_info(dev, "firmware update succeeded.\n");
+	return ret ? ret : count;
+}
+
+static ssize_t elan_sysfs_calibrate(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct elan_tp_data *data = dev_get_drvdata(dev);
+	/* start calibarate cmd */
+	u8 smbus_cmd[4] = {0x00, 0x08, 0x00, 0x01};
+	int tries = 20;
+	int ret = 0;
+	val[0] = 0;
+
+	disable_irq(data->irq);
+	elan_enable_calibrate(data);
+	if (data->smbus)
+		i2c_smbus_write_block_data(data->client,
+				ETP_SMBUS_IAP_CMD, 4, smbus_cmd);
+	else
+		elan_i2c_write_cmd(data->client,
+					ETP_I2C_CALIBRATE_CMD, 1);
+
+	do {
+		/* wait 250ms and check finish or not */
+		msleep(250);
+
+		if (data->smbus)
+			i2c_smbus_read_block_data(data->client,
+					ETP_SMBUS_CALIBRATE_QUERY, val);
+		else
+			elan_i2c_read_block(data->client,
+					ETP_I2C_CALIBRATE_CMD, val, 1);
+
+		/* calibrate finish */
+		if (val[0] == 0)
+			break;
+	} while (--tries);
+
+	elan_disable_calibrate(data);
+	enable_irq(data->irq);
+
+	if (tries == 0) {
+		dev_err(dev, "Failed to calibrate. Timeout.\n");
+		ret = -ETIMEDOUT;
+	}
+	return sprintf(buf, "calibration finish\n");
+}
+
+
+static ssize_t elan_sysfs_read_baseline(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	struct elan_tp_data *data = dev_get_drvdata(dev);
+	int max_baseline, min_baseline;
+
+	disable_irq(data->irq);
+	elan_enable_calibrate(data);
+	msleep(250);
+	max_baseline = elan_get_max_baseline(data);
+	min_baseline = elan_get_min_baseline(data);
+	elan_disable_calibrate(data);
+	enable_irq(data->irq);
+	return sprintf(buf, "max:%d min:%d\n", max_baseline, min_baseline);
+}
+
+static ssize_t elan_sysfs_reinitialize(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct elan_tp_data *data = dev_get_drvdata(dev);
+	int ret;
+
+	disable_irq(data->irq);
+	ret = elan_initialize(data);
+	enable_irq(data->irq);
+
+	if (ret < 0)
+		return sprintf(buf, "reinitialize fail\n");
+
+	return sprintf(buf, "reinitialize success\n");
+}
+
+static DEVICE_ATTR(unique_id, S_IRUGO, elan_sysfs_read_unique_id, NULL);
+static DEVICE_ATTR(firmware_version, S_IRUGO, elan_sysfs_read_fw_ver, NULL);
+static DEVICE_ATTR(sample_version, S_IRUGO, elan_sysfs_read_sm_ver, NULL);
+static DEVICE_ATTR(driver_version, S_IRUGO, elan_sysfs_read_driver_ver, NULL);
+static DEVICE_ATTR(iap_version, S_IRUGO, elan_sysfs_read_iap_ver, NULL);
+static DEVICE_ATTR(fw_checksum, S_IRUGO, elan_sysfs_read_fw_checksum, NULL);
+static DEVICE_ATTR(open_info, S_IRUGO, elan_sysfs_enable_detailinfo, NULL);
+static DEVICE_ATTR(baseline, S_IRUGO, elan_sysfs_read_baseline, NULL);
+static DEVICE_ATTR(reinitialize, S_IRUGO, elan_sysfs_reinitialize, NULL);
+static DEVICE_ATTR(calibrate, S_IRUGO, elan_sysfs_calibrate, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, elan_sysfs_update_fw);
+
+static struct attribute *elan_sysfs_entries[] = {
+	&dev_attr_unique_id.attr,
+	&dev_attr_firmware_version.attr,
+	&dev_attr_sample_version.attr,
+	&dev_attr_driver_version.attr,
+	&dev_attr_iap_version.attr,
+	&dev_attr_fw_checksum.attr,
+	&dev_attr_open_info.attr,
+	&dev_attr_baseline.attr,
+	&dev_attr_reinitialize.attr,
+	&dev_attr_calibrate.attr,
+	&dev_attr_update_fw.attr,
+	NULL,
+};
+
+static const struct attribute_group elan_sysfs_group = {
+	.attrs = elan_sysfs_entries,
+};
+
+/*****************************************************************
+* Elan isr functions
+******************************************************************
+*/
+
+static int elan_check_packet(struct elan_tp_data *data, u8 *packet)
+{
+	u8 rid;
+
+	if (data->smbus)
+		rid = packet[0];
+	else
+		rid = packet[ETP_I2C_REPORT_ID_OFFSET];
+
+	/* check report id */
+	if (rid != ETP_REPORT_ID) {
+		dev_err(&data->client->dev, "report id [%x] fail.\n", rid);
+		return -1;
+	}
+	return 0;
+}
+
+static void elan_report_absolute(struct elan_tp_data *data, u8 *packet)
+{
+	struct input_dev *input = data->input;
+	u8 *finger_data;
+	bool finger_on;
+	int pos_x, pos_y;
+	int pressure, mk_x, mk_y;
+	int i, area_x, area_y, major, minor, new_pressure;
+	int finger_count = 0;
+	int btn_click;
+	u8  tp_info;
+
+	if (data->smbus) {
+		finger_data = &packet[ETP_SMBUS_FINGER_DATA_OFFSET];
+		tp_info = packet[1];
+	} else {
+		finger_data = &packet[ETP_I2C_FINGER_DATA_OFFSET];
+		tp_info = packet[3];
+	}
+
+	btn_click = (tp_info & 0x01);
+	for (i = 0; i < ETP_MAX_FINGERS; i++) {
+		finger_on = (tp_info >> (3 + i)) & 0x01;
+
+		/* analyze touched finger raw data*/
+		if (finger_on) {
+			pos_x = ((finger_data[0] & 0xf0) << 4) |
+				finger_data[1];
+			pos_y = ((finger_data[0] & 0x0f) << 8) |
+				finger_data[2];
+			pos_y =  data->max_y - pos_y;
+			mk_x = (finger_data[3] & 0x0f);
+			mk_y = (finger_data[3] >> 4);
+			pressure = finger_data[4];
+
+			/*
+			to avoid fat finger be as palm, so reduce the
+			width x and y per trace
+			*/
+			area_x = mk_x * (data->width_x - ETP_FWIDTH_REDUCE);
+			area_y = mk_y * (data->width_y - ETP_FWIDTH_REDUCE);
+
+			major = max(area_x, area_y);
+			minor = min(area_x, area_y);
+
+			new_pressure = pressure + ETP_PRESSURE_OFFSET;
+			if (new_pressure > ETP_MAX_PRESSURE)
+				new_pressure = ETP_MAX_PRESSURE;
+
+			input_mt_slot(input, i);
+			input_mt_report_slot_state(input, MT_TOOL_FINGER,
+									true);
+			input_report_abs(input, ABS_MT_POSITION_X, pos_x);
+			input_report_abs(input, ABS_MT_POSITION_Y, pos_y);
+			input_report_abs(input, ABS_MT_PRESSURE, new_pressure);
+			input_report_abs(input, ABS_TOOL_WIDTH, mk_x);
+			input_report_abs(input, ABS_MT_TOUCH_MAJOR, major);
+			input_report_abs(input, ABS_MT_TOUCH_MINOR, minor);
+			finger_data += ETP_FINGER_DATA_LEN;
+			finger_count++;
+		} else {
+			input_mt_slot(input, i);
+			input_mt_report_slot_state(input,
+						MT_TOOL_FINGER, false);
+		}
+	}
+
+	input_report_key(input, BTN_LEFT, (btn_click == 1));
+	input_mt_report_pointer_emulation(input, true);
+	input_sync(input);
+}
+
+static irqreturn_t elan_isr(int irq, void *dev_id)
+{
+	struct elan_tp_data *data = dev_id;
+	u8 raw[ETP_MAX_REPORT_LEN];
+	int retval;
+	int report_len;
+
+	retval = mutex_lock_interruptible(&data->dbfs_mutex);
+	if (retval)
+		return IRQ_HANDLED;
+
+	/*
+	Only in I2C protocol, when IAP all page wrote finish, driver will
+	get one INT signal from high to low, and driver must get 0000
+	to confirm IAP is finished.
+	*/
+	if (data->updated_fw) {
+		retval = i2c_master_recv(data->client, raw,
+						ETP_INF_LENGTH);
+		if (retval == 2 && !le16_to_cpup((__le16 *)raw)) {
+			dev_info(&data->client->dev,
+				"reinitializing after F/W update...");
+			elan_initialize(data);
+		}
+		data->updated_fw = false;
+		goto elan_isr_end;
+	}
+
+	if (data->smbus) {
+		report_len = ETP_SMBUS_REPORT_LEN;
+		retval = i2c_smbus_read_block_data(data->client,
+					ETP_SMBUS_PACKET_QUERY, raw);
+	} else {
+		report_len = ETP_I2C_REPORT_LEN;
+		retval = i2c_master_recv(data->client, raw, report_len);
+	}
+
+	if (retval != report_len) {
+		dev_err(&data->client->dev, "wrong packet len(%d)", retval);
+		goto elan_isr_end;
+	}
+
+	if (elan_check_packet(data, raw) < 0) {
+		dev_err(&data->client->dev, "wrong packet format.");
+		goto elan_isr_end;
+	}
+	elan_report_absolute(data, raw);
+	data->dbfs_buffer.bfetch = false;
+	memcpy(data->dbfs_buffer.buffer, raw, report_len);
+
+elan_isr_end:
+	mutex_unlock(&data->dbfs_mutex);
+	return IRQ_HANDLED;
+}
+
+
+static int elan_input_dev_create(struct elan_tp_data *data)
+{
+	struct i2c_client *client = data->client;
+	struct input_dev *input;
+	unsigned int x_res, y_res;
+	int ret;
+
+	data->input = input = input_allocate_device();
+	if (!input)
+		return -ENOMEM;
+	input->name = "Elan Touchpad";
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = &data->client->dev;
+
+	__set_bit(INPUT_PROP_POINTER, input->propbit);
+	__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
+	__set_bit(EV_KEY, input->evbit);
+	__set_bit(EV_ABS, input->evbit);
+
+	__set_bit(BTN_LEFT, input->keybit);
+	__set_bit(BTN_TOUCH, input->keybit);
+	__set_bit(BTN_TOOL_FINGER, input->keybit);
+	__set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
+	__set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
+	__set_bit(BTN_TOOL_QUADTAP, input->keybit);
+	__set_bit(BTN_TOOL_QUINTTAP, input->keybit);
+
+	__set_bit(ABS_MT_TOUCH_MAJOR, input->absbit);
+	__set_bit(ABS_MT_TOUCH_MINOR, input->absbit);
+	__set_bit(ABS_MT_POSITION_X, input->absbit);
+	__set_bit(ABS_MT_POSITION_Y, input->absbit);
+
+	data->unique_id = elan_get_unique_id(data);
+	data->fw_version = elan_get_fw_version(data);
+	data->sm_version = elan_get_sm_version(data);
+	data->iap_version = elan_get_iap_version(data);
+	data->max_x = elan_get_x_max(data);
+	data->max_y = elan_get_y_max(data);
+	data->width_x = data->max_x / elan_get_x_tracenum(data);
+	data->width_y = data->max_y / elan_get_y_tracenum(data);
+	x_res = elan_get_x_resolution(data);
+	y_res = elan_get_y_resolution(data);
+
+	dev_info(&client->dev,
+		"Elan Touchpad Information:\n"
+		"    Module unique ID:  0x%04x\n"
+		"    Firmware Version:  0x%04x\n"
+		"    Sample Version:  0x%04x\n"
+		"    IAP Version:  0x%04x\n"
+		"    Max ABS X,Y:   %d,%d\n"
+		"    Width X,Y:   %d,%d\n"
+		"    Resolution X,Y:   %d,%d (dots/mm)\n",
+		data->unique_id,
+		data->fw_version,
+		data->sm_version,
+		data->iap_version,
+		data->max_x, data->max_y,
+		data->width_x, data->width_y,
+		(char)x_res, (char)y_res);
+
+	input_set_abs_params(input, ABS_X, 0, data->max_x, 0, 0);
+	input_set_abs_params(input, ABS_Y, 0, data->max_y, 0, 0);
+	input_abs_set_res(input, ABS_X, x_res);
+	input_abs_set_res(input, ABS_Y, y_res);
+	input_set_abs_params(input, ABS_PRESSURE, 0, ETP_MAX_PRESSURE, 0, 0);
+	input_set_abs_params(input, ABS_TOOL_WIDTH, 0, ETP_FINGER_WIDTH, 0, 0);
+
+	/* handle pointer emulation and unused slots in core */
+	ret = input_mt_init_slots(input, ETP_MAX_FINGERS,
+				  INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED);
+	if (ret) {
+		dev_info(&client->dev, "allocate MT slots failed, %d\n", ret);
+		goto err_free_device;
+	}
+	input_set_abs_params(input, ABS_MT_POSITION_X, 0, data->max_x, 0, 0);
+	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, data->max_y, 0, 0);
+	input_abs_set_res(input, ABS_MT_POSITION_X, x_res);
+	input_abs_set_res(input, ABS_MT_POSITION_Y, y_res);
+	input_set_abs_params(input, ABS_MT_PRESSURE, 0,
+				ETP_MAX_PRESSURE, 0, 0);
+	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0,
+		ETP_FINGER_WIDTH * max(data->width_x, data->width_y), 0, 0);
+	input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0,
+		ETP_FINGER_WIDTH * min(data->width_x, data->width_y), 0, 0);
+
+	/* Register the device in input subsystem */
+	ret = input_register_device(input);
+	if (ret) {
+		dev_err(&client->dev, "input device register failed, %d\n",
+			ret);
+		goto err_free_device;
+	}
+
+	return 0;
+
+err_free_device:
+	input_free_device(input);
+	return ret;
+}
+
+static u8 elan_check_adapter_functionality(struct i2c_client *client)
+{
+	u8 ret = ELAN_ADAPTER_FUNC_NONE;
+
+	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+		ret |= ELAN_ADAPTER_FUNC_I2C;
+	if (i2c_check_functionality(client->adapter,
+					I2C_FUNC_SMBUS_BYTE_DATA |
+					I2C_FUNC_SMBUS_BLOCK_DATA |
+					I2C_FUNC_SMBUS_I2C_BLOCK))
+		ret |= ELAN_ADAPTER_FUNC_SMBUS;
+	return ret;
+}
+
+static int elan_probe(struct i2c_client *client,
+				    const struct i2c_device_id *dev_id)
+{
+	struct elan_tp_data *data;
+	int ret;
+	u8 adapter_func;
+	union i2c_smbus_data dummy;
+	struct device *dev = &client->dev;
+
+	adapter_func = elan_check_adapter_functionality(client);
+	if (adapter_func == ELAN_ADAPTER_FUNC_NONE) {
+		dev_err(dev, "not a supported I2C/SMBus adapter\n");
+		return -EIO;
+	}
+
+	/* Make sure there is something at this address */
+	if (dev->of_node && i2c_smbus_xfer(client->adapter, client->addr, 0,
+			 I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0)
+		return -ENODEV;
+
+	data = kzalloc(sizeof(struct elan_tp_data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	/* check protocol type */
+	if (adapter_func == ELAN_ADAPTER_FUNC_SMBUS)
+		data->smbus = true;
+	else
+		data->smbus = false;
+	data->client = client;
+	data->updated_fw = false;
+	data->enable_detail_info = false;
+	data->irq = client->irq;
+
+	ret = elan_dbfs_init(data);
+	if (ret < 0) {
+		dev_err(&client->dev, "error create elan debugfs.\n");
+		goto err_dbfs_init;
+	}
+	ret = request_threaded_irq(client->irq, NULL, elan_isr,
+				  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				  client->name, data);
+	if (ret < 0) {
+		dev_err(&client->dev, "cannot register irq=%d\n",
+							 client->irq);
+		goto err_irq;
+	}
+
+	/* initial elan touch pad */
+	ret = elan_initialize(data);
+	if (ret < 0)
+		goto err_init;
+
+	/* create input device */
+	ret = elan_input_dev_create(data);
+	if (ret < 0)
+		goto err_input_dev;
+
+	device_init_wakeup(&client->dev, 1);
+	ret = sysfs_create_group(&client->dev.kobj, &elan_sysfs_group);
+	if (ret < 0) {
+		dev_err(&client->dev, "cannot register dev attribute %d", ret);
+		goto err_create_group;
+	}
+	i2c_set_clientdata(client, data);
+	return 0;
+
+err_create_group:
+	input_free_device(data->input);
+err_input_dev:
+err_init:
+	free_irq(data->irq, data);
+err_irq:
+	debugfs_remove_recursive(data->dbfs_root);
+	mutex_destroy(&data->dbfs_mutex);
+err_dbfs_init:
+	kfree(data);
+	dev_err(&client->dev, "Elan Trackpad probe fail!\n");
+	return ret;
+}
+
+static int elan_remove(struct i2c_client *client)
+{
+	struct elan_tp_data *data = i2c_get_clientdata(client);
+
+	free_irq(data->irq, data);
+	debugfs_remove_recursive(data->dbfs_root);
+	mutex_destroy(&data->dbfs_mutex);
+
+	input_free_device(data->input);
+	input_unregister_device(data->input);
+	kfree(data);
+	sysfs_remove_group(&client->dev.kobj, &elan_sysfs_group);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int elan_suspend(struct device *dev)
+{
+	int ret;
+	struct elan_tp_data *data = dev_get_drvdata(dev);
+
+	disable_irq(data->irq);
+	if (data->smbus)
+		ret = i2c_smbus_write_byte(data->client,
+					ETP_SMBUS_DISABLE_TP);
+	else
+		ret = elan_i2c_sleep(data->client);
+
+	if (ret < 0) {
+		dev_err(dev, "suspend mode failed, %d\n", ret);
+	} else {
+		if (device_may_wakeup(dev))
+			data->irq_wake = (enable_irq_wake(data->irq) == 0);
+	}
+	return 0;
+}
+
+static int elan_resume(struct device *dev)
+{
+	int ret = 0;
+	struct elan_tp_data *data = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev) && data->irq_wake)
+		disable_irq_wake(data->irq);
+
+	ret = elan_initialize(data);
+	if (ret < 0)
+		dev_err(dev, "resume active power failed, %d\n", ret);
+
+	enable_irq(data->irq);
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(elan_pm_ops, elan_suspend, elan_resume);
+
+static const struct i2c_device_id elan_id[] = {
+	{ DRIVER_NAME, 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, elan_id);
+
+static struct i2c_driver elan_driver = {
+	.driver = {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+		.pm	= &elan_pm_ops,
+	},
+	.probe		= elan_probe,
+	.remove		= elan_remove,
+	.id_table	= elan_id,
+};
+
+
+static int __init elan_init(void)
+{
+	int ret;
+	ret = i2c_add_driver(&elan_driver);
+	if (ret) {
+		pr_err("elan driver register FAILED.\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static void __exit elan_exit(void)
+{
+	i2c_del_driver(&elan_driver);
+}
+
+module_init(elan_init);
+module_exit(elan_exit);
+
+MODULE_AUTHOR("Duson Lin <dusonlin@emc.com.tw>");
+MODULE_DESCRIPTION("Elan I2C/SMBus Touchpad driver");
+MODULE_LICENSE("GPL");
-- 
1.7.10.4

^ permalink raw reply related

* Re: input question: ambient light sensor button
From: Pali Rohár @ 2013-11-22 11:13 UTC (permalink / raw)
  To: Benjamin Tissoires, Dmitry Torokhov
  Cc: Jiri Kosina, linux-kernel@vger.kernel.org, linux-input
In-Reply-To: <CAN+gG=HwuES-H4y5=_VcEQg4OiE2NYmra2+MzsbPusiOEq7Qsw@mail.gmail.com>

[-- Attachment #1: Type: Text/Plain, Size: 1857 bytes --]

On Wednesday 20 November 2013 16:59:42 Benjamin Tissoires wrote:
> Hi,
> 
> On Wed, Nov 20, 2013 at 9:50 AM, Pali Rohár <pali.rohar@gmail.com> wrote:
> >> > > I guess we need patch adding
> >> > > 
> >> > >   #define KEY_ALS_TOGGLE  0x230
> >> > > 
> >> > > Thanks.
> >> > 
> >> > So there is no good key for als yet?
> >> 
> >> No, but if you send me a patch adding it I will gladly
> >> apply it.
> >> 
> >> Thanks.
> > 
> > Ok, here is patch:
> > 
> > Add key code for ambient light sensor button
> > 
> > More notebooks have special button for enabling/disabling
> > ambient light sensor. This patch adding new als code to
> > input.h header file.
> > 
> > Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
> > 
> > diff --git a/include/uapi/linux/input.h
> > b/include/uapi/linux/input.h index a372627..1562f10 100644
> > --- a/include/uapi/linux/input.h
> > +++ b/include/uapi/linux/input.h
> > @@ -719,6 +719,8 @@ struct input_keymap_entry {
> > 
> >  #define BTN_DPAD_LEFT          0x222
> >  #define BTN_DPAD_RIGHT         0x223
> > 
> > +#define KEY_ALS_TOGGLE         0x230
> 
> Could you just add a comment explaining that ALS is ambiant
> light sensor? Otherwise, I'm sure someone else will use this
> event code for an other thing... :)
> 
> Cheers,
> Benjamin

Ok, here is new diff with comment:

diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
index a372627..7bacdb5 100644
--- a/include/uapi/linux/input.h
+++ b/include/uapi/linux/input.h
@@ -719,6 +719,8 @@ struct input_keymap_entry {
 #define BTN_DPAD_LEFT		0x222
 #define BTN_DPAD_RIGHT		0x223
 
+#define KEY_ALS_TOGGLE		0x230	/* Ambient light sensor */
+
 #define BTN_TRIGGER_HAPPY		0x2c0
 #define BTN_TRIGGER_HAPPY1		0x2c0
 #define BTN_TRIGGER_HAPPY2		0x2c1


-- 
Pali Rohár
pali.rohar@gmail.com

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply related

* Re: [appleir] BUG: unable to handle kernel NULL pointer dereference
From: Jiri Kosina @ 2013-11-22 12:39 UTC (permalink / raw)
  To: Luis Henriques
  Cc: James Henstridge, Benjamin Tissoires, linux-kernel, linux-input,
	Fabien André, Bastien Nocera
In-Reply-To: <20131121101316.GA3394@hercules.my.domain>

On Thu, 21 Nov 2013, Luis Henriques wrote:

> > > Sorry for the delays in testing out the patch.  I have tried a kernel
> > > with the patch applied, and can no longer reproduce the oops.  The
> > > hid-appleir driver appears to be working correctly, generating key
> > > press events in response to the remote, and LIRC functions correctly
> > > via hiddev.
> > > 
> > > Thanks for the everyone's help with this.
> > 
> > Applied, thanks.
> 
> Hi Jiri,
> 
> Since this fixes an issue in a 3.11 kernel, could you please tag this
> commit for stable>=3.11?  If its too late, I can send the request to
> stable@ once this patch is merged.

Thanks for noticing. It's too late to add the tag, so if you submit it to 
-stable once it's in Linus' tree, I'll appreciate it; otherwise I'll try 
to remember to do that myself.

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply

* Re: [appleir] BUG: unable to handle kernel NULL pointer dereference
From: Luis Henriques @ 2013-11-22 12:52 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: James Henstridge, Benjamin Tissoires, linux-kernel, linux-input,
	Fabien André, Bastien Nocera
In-Reply-To: <alpine.LNX.2.00.1311221339110.22082@pobox.suse.cz>

On Fri, Nov 22, 2013 at 01:39:47PM +0100, Jiri Kosina wrote:
> On Thu, 21 Nov 2013, Luis Henriques wrote:
> 
> > > > Sorry for the delays in testing out the patch.  I have tried a kernel
> > > > with the patch applied, and can no longer reproduce the oops.  The
> > > > hid-appleir driver appears to be working correctly, generating key
> > > > press events in response to the remote, and LIRC functions correctly
> > > > via hiddev.
> > > > 
> > > > Thanks for the everyone's help with this.
> > > 
> > > Applied, thanks.
> > 
> > Hi Jiri,
> > 
> > Since this fixes an issue in a 3.11 kernel, could you please tag this
> > commit for stable>=3.11?  If its too late, I can send the request to
> > stable@ once this patch is merged.
> 
> Thanks for noticing. It's too late to add the tag, so if you submit it to 
> -stable once it's in Linus' tree, I'll appreciate it; otherwise I'll try 
> to remember to do that myself.

Sure, no problem.  I'll keep an eye on this.

Cheers,
--
Luis

^ permalink raw reply

* [PATCH v2] input: don't call input_dev_release_keys() in resume
From: Oskar Andero @ 2013-11-22 13:27 UTC (permalink / raw)
  To: linux-kernel, linux-input
  Cc: Dmitry Torokhov, Julian Shandorov, Aleksej Makarov, Oskar Andero

From: Aleksej Makarov <aleksej.makarov@sonymobile.com>

When waking up the platform by pressing a specific key, sending a
release on that key makes it impossible to react on the event in
user-space. This is fixed by moving the input_reset_device() call to
resume instead.

Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Reviewed-by: Radovan Lekanovic <radovan.lekanovic@sonymobile.com>
Signed-off-by: Aleksej Makarov <aleksej.makarov@sonymobile.com>
Signed-off-by: Oskar Andero <oskar.andero@sonymobile.com>
---
 drivers/input/input.c | 11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)

diff --git a/drivers/input/input.c b/drivers/input/input.c
index 846ccdd..511d490 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -1676,22 +1676,13 @@ static int input_dev_suspend(struct device *dev)
 {
 	struct input_dev *input_dev = to_input_dev(dev);
 
-	mutex_lock(&input_dev->mutex);
-
-	if (input_dev->users)
-		input_dev_toggle(input_dev, false);
-
-	mutex_unlock(&input_dev->mutex);
+	input_reset_device(input_dev);
 
 	return 0;
 }
 
 static int input_dev_resume(struct device *dev)
 {
-	struct input_dev *input_dev = to_input_dev(dev);
-
-	input_reset_device(input_dev);
-
 	return 0;
 }
 
-- 
1.8.1.5

^ permalink raw reply related

* [PATCH] input: Add support for MMA7455L/MMA7456L 3-Axis Accelerometer
From: Alexander Shiyan @ 2013-11-22 15:53 UTC (permalink / raw)
  To: linux-input
  Cc: devicetree, Dmitry Torokhov, Rob Herring, Pawel Moll,
	Mark Rutland, Stephen Warren, Ian Campbell, Grant Likely,
	Alexander Shiyan

This patch adds support for Freescale MMA7455L/MMA7456L 3-Axis
Accelerometer connected to I2C bus. Driver can be loaded ether
with or without DT support. The basic parameters of the driver
can be changed through sysfs.

Signed-off-by: Alexander Shiyan <shc_work@mail.ru>
---
 .../devicetree/bindings/input/fsl-mma745xl.txt     |  16 +
 drivers/input/misc/Kconfig                         |   8 +
 drivers/input/misc/Makefile                        |   1 +
 drivers/input/misc/mma745xl.c                      | 490 +++++++++++++++++++++
 4 files changed, 515 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/fsl-mma745xl.txt
 create mode 100644 drivers/input/misc/mma745xl.c

diff --git a/Documentation/devicetree/bindings/input/fsl-mma745xl.txt b/Documentation/devicetree/bindings/input/fsl-mma745xl.txt
new file mode 100644
index 0000000..68feeb7
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/fsl-mma745xl.txt
@@ -0,0 +1,16 @@
+* Freescale MMA7455L/MMA7456L Three Axis Accelerometer
+
+Required properties:
+- compatible: Should contain "fsl,mma7455l".
+- reg: The I2C address of device.
+- interrupt-parent: Defines the parent interrupt controller.
+- interrupts: Should contain the IRQ specifiers for INT1 and INT2 pins.
+
+Example:
+	accelerometer: mma7455l@1d {
+		compatible = "fsl,mma7455l";
+		reg = <0x1d>;
+		interrupt-parent = <&gpio1>;
+		interrupts = <7 GPIO_ACTIVE_HIGH>,
+			     <6 GPIO_ACTIVE_HIGH>;
+	};
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 5f4967d..ecd3a50 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -176,6 +176,14 @@ config INPUT_MC13783_PWRBUTTON
 	  To compile this driver as a module, choose M here: the module
 	  will be called mc13783-pwrbutton.
 
+config INPUT_MMA745XL
+	tristate "MMA745xL - Freescale's 3-Axis, Digital Acceleration Sensor"
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  Say Y here if you want to support Freescale's MMA7455L/MMA7456L
+	  Three Axis Accelerometer through I2C interface.
+
 config INPUT_MMA8450
 	tristate "MMA8450 - Freescale's 3-Axis, 8/12-bit Digital Accelerometer"
 	depends on I2C
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 0ebfb6d..10b2c12 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_INPUT_M68K_BEEP)		+= m68kspkr.o
 obj-$(CONFIG_INPUT_MAX8925_ONKEY)	+= max8925_onkey.o
 obj-$(CONFIG_INPUT_MAX8997_HAPTIC)	+= max8997_haptic.o
 obj-$(CONFIG_INPUT_MC13783_PWRBUTTON)	+= mc13783-pwrbutton.o
+obj-$(CONFIG_INPUT_MMA745XL)		+= mma745xl.o
 obj-$(CONFIG_INPUT_MMA8450)		+= mma8450.o
 obj-$(CONFIG_INPUT_MPU3050)		+= mpu3050.o
 obj-$(CONFIG_INPUT_PCAP)		+= pcap_keys.o
diff --git a/drivers/input/misc/mma745xl.c b/drivers/input/misc/mma745xl.c
new file mode 100644
index 0000000..4e4e847
--- /dev/null
+++ b/drivers/input/misc/mma745xl.c
@@ -0,0 +1,490 @@
+/*
+ *  Driver for Freescale's 3-Axis Acceleration Sensor MMA7455L/MMA7456L
+ *
+ *  Copyright (C) 2013 Alexander Shiyan <shc_work@mail.ru>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ */
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+#include <linux/sysfs.h>
+
+#define MMA745XL_REG_XOUTL	0x00
+#define MMA745XL_REG_XOUTH	0x01
+#define MMA745XL_REG_YOUTL	0x02
+#define MMA745XL_REG_YOUTH	0x03
+#define MMA745XL_REG_ZOUTL	0x04
+#define MMA745XL_REG_ZOUTH	0x05
+#define MMA745XL_REG_XOUT8	0x06
+#define MMA745XL_REG_YOUT8	0x07
+#define MMA745XL_REG_ZOUT8	0x08
+#define MMA745XL_REG_STATUS	0x09
+# define STATUS_DRDY		(1 << 0)
+# define STATUS_DOVR		(1 << 1)
+# define STATUS_PERR		(1 << 2)
+#define MMA745XL_REG_DETSRC	0x0a
+# define DETSRC_PDZ		(1 << 2)
+# define DETSRC_PDY		(1 << 3)
+# define DETSRC_PDX		(1 << 4)
+# define DETSRC_LDZ		(1 << 5)
+# define DETSRC_LDY		(1 << 6)
+# define DETSRC_LDX		(1 << 7)
+#define MMA745XL_REG_TOUT	0x0b
+#define MMA745XL_REG_I2CAD	0x0d
+#define MMA745XL_REG_USRINF	0x0e
+#define MMA745XL_REG_WHOAMI	0x0f
+# define WHOAMI_MMA745XL	0x55
+#define MMA745XL_REG_XOFFL	0x10
+#define MMA745XL_REG_XOFFH	0x11
+#define MMA745XL_REG_YOFFL	0x12
+#define MMA745XL_REG_YOFFH	0x13
+#define MMA745XL_REG_ZOFFL	0x14
+#define MMA745XL_REG_ZOFFH	0x15
+#define MMA745XL_REG_MCTL	0x16
+# define MCTL_MODE_STANDBY	(0 << 0)
+# define MCTL_MODE_MEASUREMENT	(1 << 0)
+# define MCTL_MODE_LEVELDET	(2 << 0)
+# define MCTL_MODE_PULSEDET	(3 << 0)
+# define MCTL_MODE_MASK		(3 << 0)
+# define MCTL_GLVL_8		(0 << 2)
+# define MCTL_GLVL_2		(1 << 2)
+# define MCTL_GLVL_4		(2 << 2)
+# define MCTL_GLVL_MASK		(3 << 2)
+# define MCTL_STON		(1 << 4)
+# define MCTL_SPI3W		(1 << 5)
+# define MCTL_DRPD		(1 << 6)
+#define MMA745XL_REG_INTRST	0x17
+# define INTRST_CLR_INT1	(1 << 0)
+# define INTRST_CLR_INT2	(1 << 1)
+#define MMA745XL_REG_CTL1	0x18
+# define CTL1_DFBW		(1 << 7)
+#define MMA745XL_REG_CTL2	0x19
+#define MMA745XL_REG_LDTH	0x1a
+#define MMA745XL_REG_PDTH	0x1b
+#define MMA745XL_REG_PW		0x1c
+#define MMA745XL_REG_LT		0x1d
+#define MMA745XL_REG_TW		0x1e
+
+#define MMA745XL_MODE_DEF	MCTL_MODE_LEVELDET
+#define MMA745XL_MEASURE_TIME	20
+#define MMA745XL_THRESHOLD_DEF	24
+#define MMA745XL_PULSEW_DEF	6
+
+#define MMA745XL_X		(1 << 0)
+#define MMA745XL_Y		(1 << 1)
+#define MMA745XL_Z		(1 << 2)
+
+struct mma745xl {
+	struct regmap		*regmap;
+	struct regmap_config	regcfg;
+	unsigned int		mode;
+};
+
+static int mma745xl_measure_axis(struct mma745xl *priv, unsigned int reg,
+				 int *val)
+{
+	unsigned int tmpl = 0, tmph = 0;
+	int ret;
+
+	ret = regmap_read(priv->regmap, reg, &tmpl);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(priv->regmap, reg + 1, &tmph);
+	if (!ret) {
+		*val = ((tmph & 0x03) << 8) | (tmpl & 0xff);
+		/* Make signed variable */
+		if (*val & 0x200)
+			*val -= 0x400;
+	}
+
+	return ret;
+}
+
+static void mma745xl_measure(struct input_dev *input, unsigned int mask)
+{
+	struct mma745xl *priv = input_get_drvdata(input);
+	int x, y, z;
+
+	if (mask & MMA745XL_X)
+		if (!mma745xl_measure_axis(priv, MMA745XL_REG_XOUTL, &x))
+			input_report_abs(input, ABS_X, x);
+	if (mask & MMA745XL_Y)
+		if (!mma745xl_measure_axis(priv, MMA745XL_REG_YOUTL, &y))
+			input_report_abs(input, ABS_Y, y);
+	if (mask & MMA745XL_Z)
+		if (!mma745xl_measure_axis(priv, MMA745XL_REG_ZOUTL, &z))
+			input_report_abs(input, ABS_Z, z);
+
+	input_sync(input);
+}
+
+static irqreturn_t mma745xl_interrupt(int irq, void *dev_id)
+{
+	struct input_dev *input = dev_id;
+	struct mma745xl *priv = input_get_drvdata(input);
+	unsigned int val;
+
+	if (!regmap_read(priv->regmap, MMA745XL_REG_DETSRC, &val)) {
+		unsigned int mask;
+
+		mask = (val & (DETSRC_LDX | DETSRC_PDX)) ? MMA745XL_X : 0;
+		mask |= (val & (DETSRC_LDY | DETSRC_PDY)) ? MMA745XL_Y : 0;
+		mask |= (val & (DETSRC_LDZ | DETSRC_PDZ)) ? MMA745XL_Z : 0;
+		mma745xl_measure(input, mask);
+	}
+
+	/* Clear interrupt flags */
+	regmap_write(priv->regmap, MMA745XL_REG_INTRST,
+		     INTRST_CLR_INT1 | INTRST_CLR_INT2);
+	/* Enable pins to trigger */
+	regmap_write(priv->regmap, MMA745XL_REG_INTRST, 0);
+
+	return IRQ_HANDLED;
+}
+
+static int mma745xl_open(struct input_dev *input)
+{
+	struct mma745xl *priv = input_get_drvdata(input);
+	unsigned long tmp;
+	unsigned int val;
+	int ret;
+
+	/* Get initial values */
+	ret = regmap_update_bits(priv->regmap, MMA745XL_REG_MCTL,
+				 MCTL_MODE_MASK, MCTL_MODE_MEASUREMENT);
+	if (ret)
+		return ret;
+
+	tmp = jiffies + msecs_to_jiffies(MMA745XL_MEASURE_TIME);
+	for (;;) {
+		ret = regmap_read(priv->regmap, MMA745XL_REG_STATUS, &val);
+		if (ret)
+			return ret;
+		if (val == STATUS_DRDY)
+			break;
+		if (val & (STATUS_DOVR | STATUS_PERR)) {
+			/* Clear status */
+			ret = regmap_read(priv->regmap,
+					  MMA745XL_REG_XOUTL, &val);
+			if (ret)
+				return ret;
+			/* Restart measuring */
+			tmp = jiffies + msecs_to_jiffies(MMA745XL_MEASURE_TIME);
+		}
+		if (time_after(jiffies, tmp)) {
+			dev_err(&input->dev, "Measure timeout\n");
+			return -ETIMEDOUT;
+		}
+	}
+
+	mma745xl_measure(input, MMA745XL_X | MMA745XL_Y | MMA745XL_Z);
+
+	/* Go to desired mode */
+	return regmap_update_bits(priv->regmap, MMA745XL_REG_MCTL,
+				  MCTL_MODE_MASK, priv->mode);
+}
+
+static void mma745xl_close(struct input_dev *input)
+{
+	struct mma745xl *priv = input_get_drvdata(input);
+
+	regmap_update_bits(priv->regmap, MMA745XL_REG_MCTL, MCTL_MODE_MASK, 0);
+}
+
+static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct input_dev *input = dev_get_drvdata(dev);
+	struct mma745xl *priv = input_get_drvdata(input);
+
+	switch (priv->mode) {
+	case MCTL_MODE_LEVELDET:
+		return sprintf(buf, "level\n");
+	case MCTL_MODE_PULSEDET:
+		return sprintf(buf, "pulse\n");
+	default:
+		break;
+	}
+
+	return sprintf(buf, "unknown\n");
+}
+
+static ssize_t mode_store(struct device *dev, struct device_attribute *attr,
+			  const char *buf, size_t count)
+{
+	struct input_dev *input = dev_get_drvdata(dev);
+	struct mma745xl *priv = input_get_drvdata(input);
+
+	if (!strncmp(buf, "level", count))
+		priv->mode = MCTL_MODE_LEVELDET;
+	else if (!strncmp(buf, "pulse", count))
+		priv->mode = MCTL_MODE_PULSEDET;
+	else
+		return -EINVAL;
+
+	return count;
+}
+
+static ssize_t level_show(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct input_dev *input = dev_get_drvdata(dev);
+	struct mma745xl *priv = input_get_drvdata(input);
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(priv->regmap, MMA745XL_REG_MCTL, &val);
+	if (ret)
+		return ret;
+
+	switch (val & MCTL_GLVL_MASK) {
+	case MCTL_GLVL_2:
+		return sprintf(buf, "2\n");
+	case MCTL_GLVL_4:
+		return sprintf(buf, "4\n");
+	case MCTL_GLVL_8:
+		return sprintf(buf, "8\n");
+	default:
+		break;
+	}
+
+	return sprintf(buf, "unknown\n");
+}
+
+static ssize_t level_store(struct device *dev, struct device_attribute *attr,
+			   const char *buf, size_t count)
+{
+	struct input_dev *input = dev_get_drvdata(dev);
+	struct mma745xl *priv = input_get_drvdata(input);
+	unsigned long tmp;
+	unsigned int val;
+	int ret;
+
+	ret = kstrtoul(buf, 10, &tmp);
+	if (ret)
+		return ret;
+
+	switch (tmp) {
+	case 2:
+		val = MCTL_GLVL_2;
+		break;
+	case 4:
+		val = MCTL_GLVL_4;
+		break;
+	case 8:
+		val = MCTL_GLVL_8;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = regmap_update_bits(priv->regmap, MMA745XL_REG_MCTL,
+				 MCTL_GLVL_MASK, val);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static ssize_t threshold_show(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	struct input_dev *input = dev_get_drvdata(dev);
+	struct mma745xl *priv = input_get_drvdata(input);
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(priv->regmap, MMA745XL_REG_LDTH, &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%d\n", val & 0x7f);
+}
+
+static ssize_t threshold_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct input_dev *input = dev_get_drvdata(dev);
+	struct mma745xl *priv = input_get_drvdata(input);
+	unsigned long tmp;
+	int ret;
+
+	ret = kstrtoul(buf, 10, &tmp);
+	if (ret)
+		return ret;
+	if (tmp > 0x7f)
+		return -ERANGE;
+
+	ret = regmap_write(priv->regmap, MMA745XL_REG_LDTH, tmp);
+	if (ret)
+		return ret;
+	ret = regmap_write(priv->regmap, MMA745XL_REG_PDTH, tmp);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static DEVICE_ATTR_RW(mode);
+static DEVICE_ATTR_RW(level);
+static DEVICE_ATTR_RW(threshold);
+
+static struct attribute *mma745xl_sysfs_attrs[] = {
+	&dev_attr_mode.attr,
+	&dev_attr_level.attr,
+	&dev_attr_threshold.attr,
+	NULL
+};
+
+static const struct attribute_group mma745xl_sysfs_group = {
+	.attrs	= mma745xl_sysfs_attrs,
+};
+
+static int mma745xl_probe(struct i2c_client *client,
+			  const struct i2c_device_id *id)
+{
+	struct input_dev *input;
+	unsigned int i, irq, val = 0;
+	struct mma745xl *priv;
+	int ret;
+
+	if (!client->dev.of_node)
+		return -ENOTSUPP;
+
+	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+	input = devm_input_allocate_device(&client->dev);
+	if (!priv || !input)
+		return -ENOMEM;
+
+	priv->regcfg.reg_bits		= 8;
+	priv->regcfg.val_bits		= 8;
+	priv->regcfg.cache_type		= REGCACHE_NONE;
+	priv->regcfg.max_register	= MMA745XL_REG_TW;
+	priv->regmap = devm_regmap_init_i2c(client, &priv->regcfg);
+	if (IS_ERR(priv->regmap))
+		return PTR_ERR(priv->regmap);
+
+	/* Check functionality */
+	ret = regmap_read(priv->regmap, MMA745XL_REG_WHOAMI, &val);
+	if (ret || (val != WHOAMI_MMA745XL)) {
+		dev_err(&client->dev, "Probe failed (ID=0x%02x)\n", val);
+		return ret ? : -ENODEV;
+	}
+
+	input->name		= client->name;
+	input->id.bustype	= BUS_I2C;
+	input->id.vendor	= 0x0001;
+	input->id.product	= 0x0001;
+	input->id.version	= 0x0100;
+	input->open		= mma745xl_open;
+	input->close		= mma745xl_close;
+
+	for (i = ABS_X; i <= ABS_Z; i++) {
+		input_set_capability(input, EV_ABS, i);
+		input_set_abs_params(input, i, -512, 511, 0, 0);
+	}
+
+	input_set_drvdata(input, priv);
+	dev_set_drvdata(&client->dev, input);
+
+	/* Put into standby mode, Data ready status not routed to INT1 */
+	ret = regmap_write(priv->regmap, MMA745XL_REG_MCTL, MCTL_DRPD);
+	if (ret)
+		return ret;
+	/* Set bandwidth to 125 kHz */
+	ret = regmap_write(priv->regmap, MMA745XL_REG_CTL1, CTL1_DFBW);
+	if (ret)
+		return ret;
+	/* Set detecting condition to "OR" */
+	ret = regmap_write(priv->regmap, MMA745XL_REG_CTL2, 0);
+	if (ret)
+		return ret;
+	/* Set level detection threshold limit */
+	ret = regmap_write(priv->regmap, MMA745XL_REG_LDTH,
+			   MMA745XL_THRESHOLD_DEF);
+	if (ret)
+		return ret;
+	/* Set pulse detection threshold limit */
+	ret = regmap_write(priv->regmap, MMA745XL_REG_PDTH,
+			   MMA745XL_THRESHOLD_DEF);
+	if (ret)
+		return ret;
+	/* Set pulse duration value */
+	ret = regmap_write(priv->regmap, MMA745XL_REG_PW,
+			   MMA745XL_PULSEW_DEF);
+	if (ret)
+		return ret;
+
+	priv->mode = MMA745XL_MODE_DEF;
+
+	irq = irq_of_parse_and_map(client->dev.of_node, 0);
+	if (!irq)
+		return -EINVAL;
+	ret = devm_request_threaded_irq(&client->dev, irq, NULL,
+					mma745xl_interrupt, IRQF_ONESHOT,
+					dev_name(&client->dev), input);
+	if (ret)
+		return ret;
+
+	irq = irq_of_parse_and_map(client->dev.of_node, 1);
+	if (!irq)
+		return -EINVAL;
+	ret = devm_request_threaded_irq(&client->dev, irq, NULL,
+					mma745xl_interrupt, IRQF_ONESHOT,
+					dev_name(&client->dev), input);
+	if (ret)
+		return ret;
+
+	ret = input_register_device(input);
+	if (ret)
+		return ret;
+
+	return sysfs_create_group(&client->dev.kobj, &mma745xl_sysfs_group);
+}
+
+static int mma745xl_remove(struct i2c_client *client)
+{
+	sysfs_remove_group(&client->dev.kobj, &mma745xl_sysfs_group);
+
+	return 0;
+}
+
+static const struct of_device_id __maybe_unused mma745xl_dt_ids[] = {
+	{ .compatible = "fsl,mma7455l", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, mma745xl_dt_ids);
+
+static const struct i2c_device_id mma745xl_ids[] = {
+	{ .name = "mma7455l", },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, mma745xl_ids);
+
+static struct i2c_driver mma745xl_driver = {
+	.driver		= {
+		.name		= "mma745xl",
+		.owner		= THIS_MODULE,
+		.of_match_table	= of_match_ptr(mma745xl_dt_ids),
+	},
+	.id_table	= mma745xl_ids,
+	.probe		= mma745xl_probe,
+	.remove		= mma745xl_remove,
+};
+module_i2c_driver(mma745xl_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("MMA745xL 3-Axis Acceleration Sensor");
-- 
1.8.3.2


^ permalink raw reply related

* Re: [PATCH 1/3] Input: Fixed ABS_MT_TOUCH_MINOR scale factor in BCM5974 multitouch driver
From: Friedrich Schöller @ 2013-11-22 15:54 UTC (permalink / raw)
  To: Henrik Rydberg; +Cc: Dmitry Torokhov, linux-input, linux-kernel
In-Reply-To: <528DCCBD.80009@euromail.se>

Hello Henrik,

>> On wellspring3 devices ABS_MT_TOUCH_MINOR was sometimes reported bigger than
>> ABS_MT_TOUCH_MAJOR. This is fixed by rescaling ABS_MT_TOUCH_MINOR by a factor of
>> 0.85 instead of 2. Excessive tapping on the trackpad shows this to be the right
>> value. Circular touches should now lead to values for ABS_MT_TOUCH_MAJOR and
>> ABS_MT_TOUCH_MINOR that are similar, with ABS_MT_TOUCH_MINOR never greater than
>> ABS_MT_TOUCH_MAJOR.
>> ---
>>  drivers/input/mouse/bcm5974.c | 20 +++++++++++++++++---
>>  1 file changed, 17 insertions(+), 3 deletions(-)
>
> The major/minor scales are following the aspect ratio of the device, and as such
> it could happen that minor > major. Most userland drivers do not use the finger
> width limits, which are estimates, but only the device axes limit, which are
> accurate.

I know you wrote it, but this seems to me to contradict the
documentation on the multitouch protocol:
"In addition to the MAJOR parameters, the oval shape of the touch and finger
regions can be described by adding the MINOR parameters, such that MAJOR
and MINOR are the major and minor axis of an ellipse."

Why do the limits on the device axes, which are oriented horizontally
and vertically, have any influence on the major and minor parameters
which can be oriented arbitrarily?

Or let me phrase it more pragmatically: How can a userland application
figure out the factor of 0.425 (from 0.85 / 2) by which it has to
multiply ABS_MT_TOUCH_MINOR?

As a side note: The WIDTH_MAJOR and WIDTH_MINOR parameters are already
well behaved in the sense that WIDTH_MINOR is always smaller.

> Also, we cannot have floats in the kernel.

Okay a fraction would do the trick.

Thanks,
Friedrich

^ permalink raw reply

* Re: [PATCH] Input: Add new driver for GPIO beeper
From: Alexander Shiyan @ 2013-11-22 17:28 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Stephen Warren, Mark Rutland,
	linux-input-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	rob.herring-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org, Pawel Moll,
	Ian Campbell
In-Reply-To: <20131119213239.GC25784-WlK9ik9hQGAhIp7JRqBPierSzoNAToWh@public.gmane.org>

On Tue, 19 Nov 2013 13:32:39 -0800
Dmitry Torokhov <dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:

> > >>>> This patch adds a new driver for the beeper controlled via GPIO pin.
> > >>>> The driver does not depend on the architecture and is positioned as
> > >>>> a replacement for the specific drivers that are used for this function.
> > >>>>
> > >>>> Signed-off-by: Alexander Shiyan <shc_work-JGs/UdohzUI@public.gmane.org>
> > >> ...
> > >>>> diff --git a/Documentation/devicetree/bindings/input/gpio-beeper.txt b/Documentation/devicetree/bindings/input/gpio-beeper.txt
> > >> ...
> > >>>> +Example:
> > >>>> +
> > >>>> +beeper: input@0 {
> > >>>> +	compatible = "gpio-beeper";
> > >>>> +	reg = <0>;
> > >>>> +	gpios = <&gpio3 23 0>;
> > >>>> +};
> > >>>
> > >>> What are the reg / unit-address for?
> > >>
> > >> Just an example from "simple-bus" container.
> > > 
> > > If they have no meaning, they should go. They're unnecessary and make
> > > things more confusing.
> > > 
> > > I'd expect the example to be:
> > > 
> > > beeper: beeper {
> > > 	compatible = "gpio-beeper";
> > > 	gpios - <&gpio3 23 0>;
> > > };
> > > 
> > > And if we have multiple beepers, something like:
> > > 
> > > beeper0: beeper0 { ... };
> > > beeper1: beeper1 { ... };
> > 
> > DT node names aren't meant to encode identity though. What we've done in
> > the past for nodes without a reg where multiple instances were desired
> > is to put them into simple-bus and add a reg, so:
> > 
> > beeper0: beeper@0 { reg = <0>; ... };
> > beeper1: beeper@1 { reg = <1>; ... };
> > 
> > Of course, if there's only one of them, then it could just be "beeper"
> > with no reg. The binding and example should probably reflect that simple
> > case.
> 
> So do we have an agreement on bindings? Otherwise the driver looks good
> to me.

I'll send v2 of this patch.
Thanks.

-- 
Alexander Shiyan <shc_work-JGs/UdohzUI@public.gmane.org>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [git pull] Input updates for 3.13-rc0 (2nd)
From: Dmitry Torokhov @ 2013-11-22 22:57 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: linux-kernel, linux-input

[-- Attachment #1: Type: text/plain, Size: 1293 bytes --]

Hi Linus,

Please pull from:

	git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git for-linus
or
	master.kernel.org:/pub/scm/linux/kernel/git/dtor/input.git for-linus

to receive 2nd round of updates for the input subsystem. You will get a
new driver for Surface 2.0/Pixelsense touchscreen and a couple of driver
fixups.

Changelog:
---------

Dan Carpenter (1):
      Input: hp_sdc_rtc - unlock on error in hp_sdc_rtc_read_i8042timer()

Dmitry Torokhov (1):
      Input: atmel-wm97xx - fix compile error

Florian Echtler (1):
      Input: add sur40 driver for Samsung SUR40 (aka MS Surface 2.0/Pixelsense)

Haiyang Zhang (1):
      MAINTAINERS - add keyboard driver to Hyper-V file list

Xie XiuQi (1):
      Input: cyttsp4 -  remove unnecessary work pending test


Diffstat:
--------

 drivers/input/misc/hp_sdc_rtc.c          |   5 +-
 drivers/input/touchscreen/Kconfig        |  11 +
 drivers/input/touchscreen/Makefile       |   1 +
 drivers/input/touchscreen/atmel-wm97xx.c |   2 +-
 drivers/input/touchscreen/cyttsp4_core.c |   3 +-
 drivers/input/touchscreen/sur40.c        | 466 +++++++++++++++++++++++++++++++
 6 files changed, 484 insertions(+), 4 deletions(-)
 create mode 100644 drivers/input/touchscreen/sur40.c

-- 
Dmitry


[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCHv4 0/5] HID: sony: Dualshock3 USB and LED support
From: simon @ 2013-11-23  2:35 UTC (permalink / raw)
  To: Jiri Kosina; +Cc: Sven Eckelmann, linux-input
In-Reply-To: <alpine.LNX.2.00.1311201002360.31451@pobox.suse.cz>

> On Tue, 19 Nov 2013, Sven Eckelmann wrote:
>
>> this is a resent of the patchset "[PATCHv3 0/5] HID: sony: Dualshock3
>> USB FF
>> deadlock fix and LED support". It is necessary because the patches 2-5
>> of that
>> patchset cannot be applied anymore. This happened because the v2 of the
>> patch
>> "HID: sony: Send FF commands in non-atomic context" instead of the v3
>> was
>> applied in the repository jikos/hid.git. The differences between these
>> two
>> patch versions can now be found in the new patch "[PATCHv4 1/5] HID:
>> sony:
>> Rename rumble_* functions/variables to state_*"
>
> Queued for 3.14. Thanks,

I'm a little confused about which kernel this should be in. I believe the
first in this patch series is required for 3.13 (but does not seemed to be
included (1)).

Without this, the kernel will be subject to lockups if anyone actually
uses the force feedback.
Simon

(1) -
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/log/drivers/hid/hid-sony.c


^ permalink raw reply

* Re: [PATCHv4 0/5] HID: sony: Dualshock3 USB and LED support
From: Sven Eckelmann @ 2013-11-23  9:38 UTC (permalink / raw)
  To: simon; +Cc: Jiri Kosina, linux-input
In-Reply-To: <14946321e34bb704a90c5c95f880dd9d.squirrel@mungewell.org>

On Friday 22 November 2013 21:35:28 simon@mungewell.org wrote:
> I'm a little confused about which kernel this should be in. I believe the
> first in this patch series is required for 3.13 (but does not seemed to be
> included (1)).
> 
> Without this, the kernel will be subject to lockups if anyone actually
> uses the force feedback.
> Simon

No, the first patch is for 3.14 and the rest is also for 3.13. The fix for the
lockup is in the patch "[PATCHv3 0/5] HID: sony: Dualshock3 USB FF deadlock
fix and LED support" and its v2 version. Unfortunately, the v2 was applied for
3.13 and the first patch in this series just contains the renaming stuff
between v2 and v3 of this patch.

The pull request may still have to be made by Jiri. But it is already queued up
in his 3.13 fixes branch [1]

Kind regards,
	Sven

[1] https://git.kernel.org/cgit/linux/kernel/git/jikos/hid.git/log/?h=for-3.13/upstream-fixes


^ permalink raw reply

* [PATCH] input:keyboard: "keycode & KEY_MAX" changes some keycode value.
From: andrew.liu200917 @ 2013-11-23 13:45 UTC (permalink / raw)
  To: michael.hennerich, dmitry.torokhov, device-drivers-devel
  Cc: linux-input, linux-kernel, linux-media, Andrew Liu

From: Andrew Liu <andrew.liu200917@gmail.com>

For exmaple, keycode: KEY_OK(0x160) is changed by "and" operation with
KEY_MAX(0x2ff) to KEY_KPENTER(96).

Signed-off-by: Andrew Liu <andrew.liu200917@gmail.com>
---
 drivers/input/keyboard/adp5588-keys.c |    8 ++++++--
 drivers/input/keyboard/adp5589-keys.c |    8 ++++++--
 drivers/input/keyboard/bf54x-keys.c   |    8 ++++++--
 drivers/input/misc/pcf8574_keypad.c   |    6 +++++-
 drivers/media/pci/ttpci/av7110_ir.c   |    1 +
 5 files changed, 24 insertions(+), 7 deletions(-)

diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c
index dbd2047..1ce559e 100644
--- a/drivers/input/keyboard/adp5588-keys.c
+++ b/drivers/input/keyboard/adp5588-keys.c
@@ -535,8 +535,12 @@ static int adp5588_probe(struct i2c_client *client,
 	if (pdata->repeat)
 		__set_bit(EV_REP, input->evbit);
 
-	for (i = 0; i < input->keycodemax; i++)
-		__set_bit(kpad->keycode[i] & KEY_MAX, input->keybit);
+	for (i = 0; i < input->keycodemax; i++) {
+		if (kpad->keycode[i] > KEY_MAX)
+			kpad->keycode[i] = 0;
+		else if (kpad->keycode[i] > KEY_RESERVED)
+			__set_bit(kpad->keycode[i], input->keybit);
+	}
 	__clear_bit(KEY_RESERVED, input->keybit);
 
 	if (kpad->gpimapsize)
diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c
index 67d12b3..22ca2a5 100644
--- a/drivers/input/keyboard/adp5589-keys.c
+++ b/drivers/input/keyboard/adp5589-keys.c
@@ -991,8 +991,12 @@ static int adp5589_probe(struct i2c_client *client,
 	if (pdata->repeat)
 		__set_bit(EV_REP, input->evbit);
 
-	for (i = 0; i < input->keycodemax; i++)
-		__set_bit(kpad->keycode[i] & KEY_MAX, input->keybit);
+	for (i = 0; i < input->keycodemax; i++) {
+		if (kpad->keycode[i] > KEY_MAX)
+			kpad->keycode[i] = 0;
+		else if (kpad->keycode[i] > KEY_RESERVED)
+			__set_bit(kpad->keycode[i], input->keybit);
+	}
 	__clear_bit(KEY_RESERVED, input->keybit);
 
 	if (kpad->gpimapsize)
diff --git a/drivers/input/keyboard/bf54x-keys.c b/drivers/input/keyboard/bf54x-keys.c
index fc88fb4..96f54e7 100644
--- a/drivers/input/keyboard/bf54x-keys.c
+++ b/drivers/input/keyboard/bf54x-keys.c
@@ -288,8 +288,12 @@ static int bfin_kpad_probe(struct platform_device *pdev)
 	if (pdata->repeat)
 		__set_bit(EV_REP, input->evbit);
 
-	for (i = 0; i < input->keycodemax; i++)
-		__set_bit(bf54x_kpad->keycode[i] & KEY_MAX, input->keybit);
+	for (i = 0; i < input->keycodemax; i++) {
+		if (bf54x_kpad->keycode[i] > KEY_MAX)
+			bf54x_kpad->keycode[i] = 0;
+		else if (bf54x_kpad->keycode[i] > KEY_RESERVED)
+			__set_bit(bf54x_kpad->keycode[i], input->keybit);
+	}
 	__clear_bit(KEY_RESERVED, input->keybit);
 
 	error = input_register_device(input);
diff --git a/drivers/input/misc/pcf8574_keypad.c b/drivers/input/misc/pcf8574_keypad.c
index e373929..48829f3 100644
--- a/drivers/input/misc/pcf8574_keypad.c
+++ b/drivers/input/misc/pcf8574_keypad.c
@@ -114,8 +114,12 @@ static int pcf8574_kp_probe(struct i2c_client *client, const struct i2c_device_i
 
 	for (i = 0; i < ARRAY_SIZE(pcf8574_kp_btncode); i++) {
 		lp->btncode[i] = pcf8574_kp_btncode[i];
-		__set_bit(lp->btncode[i] & KEY_MAX, idev->keybit);
+		if (lp->btncode[i] > KEY_MAX)
+			lp->btncode[i] = 0;
+		else if (lp->btncode[i] > KEY_RESERVED)
+			__set_bit(lp->btncode[i], idev->keybit);
 	}
+	__clear_bit(KEY_RESERVED, idev->keybit);
 
 	sprintf(lp->name, DRV_NAME);
 	sprintf(lp->phys, "kp_data/input0");
diff --git a/drivers/media/pci/ttpci/av7110_ir.c b/drivers/media/pci/ttpci/av7110_ir.c
index 0e763a7..7fdac45 100644
--- a/drivers/media/pci/ttpci/av7110_ir.c
+++ b/drivers/media/pci/ttpci/av7110_ir.c
@@ -201,6 +201,7 @@ static void input_register_keys(struct infrared *ir)
 		else if (ir->key_map[i] > KEY_RESERVED)
 			set_bit(ir->key_map[i], ir->input_dev->keybit);
 	}
+	__clear_bit(KEY_RESERVED, ir->input_dev->keybit);
 
 	ir->input_dev->keycode = ir->key_map;
 	ir->input_dev->keycodesize = sizeof(ir->key_map[0]);
-- 
1.7.1

^ permalink raw reply related

* Re: [git pull] Input updates for 3.13-rc0 (2nd)
From: Florian Echtler @ 2013-11-23 15:07 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input
In-Reply-To: <20131122225700.GC12800@core.coreip.homeip.net>

[-- Attachment #1: Type: text/plain, Size: 1594 bytes --]

Hi Dmitry,

great - thanks again for your help and for amending the missing config
option. Had entirely missed that one.

BR, Florian

On 22.11.2013 23:57, Dmitry Torokhov wrote:
> Hi Linus,
> 
> Please pull from:
> 
> 	git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git for-linus
> or
> 	master.kernel.org:/pub/scm/linux/kernel/git/dtor/input.git for-linus
> 
> to receive 2nd round of updates for the input subsystem. You will get a
> new driver for Surface 2.0/Pixelsense touchscreen and a couple of driver
> fixups.
> 
> Changelog:
> ---------
> 
> Dan Carpenter (1):
>       Input: hp_sdc_rtc - unlock on error in hp_sdc_rtc_read_i8042timer()
> 
> Dmitry Torokhov (1):
>       Input: atmel-wm97xx - fix compile error
> 
> Florian Echtler (1):
>       Input: add sur40 driver for Samsung SUR40 (aka MS Surface 2.0/Pixelsense)
> 
> Haiyang Zhang (1):
>       MAINTAINERS - add keyboard driver to Hyper-V file list
> 
> Xie XiuQi (1):
>       Input: cyttsp4 -  remove unnecessary work pending test
> 
> 
> Diffstat:
> --------
> 
>  drivers/input/misc/hp_sdc_rtc.c          |   5 +-
>  drivers/input/touchscreen/Kconfig        |  11 +
>  drivers/input/touchscreen/Makefile       |   1 +
>  drivers/input/touchscreen/atmel-wm97xx.c |   2 +-
>  drivers/input/touchscreen/cyttsp4_core.c |   3 +-
>  drivers/input/touchscreen/sur40.c        | 466 +++++++++++++++++++++++++++++++
>  6 files changed, 484 insertions(+), 4 deletions(-)
>  create mode 100644 drivers/input/touchscreen/sur40.c
> 


-- 
SENT FROM MY DEC VT50 TERMINAL


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply

* Re: [PATCH] input:keyboard: "keycode & KEY_MAX" changes some keycode value.
From: Dmitry Torokhov @ 2013-11-23 18:06 UTC (permalink / raw)
  To: andrew.liu200917
  Cc: michael.hennerich, device-drivers-devel, linux-input,
	linux-kernel, linux-media
In-Reply-To: <1385214345-10385-1-git-send-email-andrew.liu200917@gmail.com>

Hi Andrew,

On Sat, Nov 23, 2013 at 09:45:45PM +0800, andrew.liu200917@gmail.com wrote:
> From: Andrew Liu <andrew.liu200917@gmail.com>
> 
> For exmaple, keycode: KEY_OK(0x160) is changed by "and" operation with
> KEY_MAX(0x2ff) to KEY_KPENTER(96).
> 
> Signed-off-by: Andrew Liu <andrew.liu200917@gmail.com>
> ---
>  drivers/input/keyboard/adp5588-keys.c |    8 ++++++--
>  drivers/input/keyboard/adp5589-keys.c |    8 ++++++--
>  drivers/input/keyboard/bf54x-keys.c   |    8 ++++++--
>  drivers/input/misc/pcf8574_keypad.c   |    6 +++++-
>  drivers/media/pci/ttpci/av7110_ir.c   |    1 +
>  5 files changed, 24 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c
> index dbd2047..1ce559e 100644
> --- a/drivers/input/keyboard/adp5588-keys.c
> +++ b/drivers/input/keyboard/adp5588-keys.c
> @@ -535,8 +535,12 @@ static int adp5588_probe(struct i2c_client *client,
>  	if (pdata->repeat)
>  		__set_bit(EV_REP, input->evbit);
>  
> -	for (i = 0; i < input->keycodemax; i++)
> -		__set_bit(kpad->keycode[i] & KEY_MAX, input->keybit);
> +	for (i = 0; i < input->keycodemax; i++) {
> +		if (kpad->keycode[i] > KEY_MAX)
> +			kpad->keycode[i] = 0;

There is no need to reset keymap entires to 0, not setting keybit is
enough.

> +		else if (kpad->keycode[i] > KEY_RESERVED)
> +			__set_bit(kpad->keycode[i], input->keybit);
> +	}
>  	__clear_bit(KEY_RESERVED, input->keybit);
>  
>  	if (kpad->gpimapsize)
> diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c
> index 67d12b3..22ca2a5 100644
> --- a/drivers/input/keyboard/adp5589-keys.c
> +++ b/drivers/input/keyboard/adp5589-keys.c
> @@ -991,8 +991,12 @@ static int adp5589_probe(struct i2c_client *client,
>  	if (pdata->repeat)
>  		__set_bit(EV_REP, input->evbit);
>  
> -	for (i = 0; i < input->keycodemax; i++)
> -		__set_bit(kpad->keycode[i] & KEY_MAX, input->keybit);
> +	for (i = 0; i < input->keycodemax; i++) {
> +		if (kpad->keycode[i] > KEY_MAX)
> +			kpad->keycode[i] = 0;
> +		else if (kpad->keycode[i] > KEY_RESERVED)
> +			__set_bit(kpad->keycode[i], input->keybit);
> +	}
>  	__clear_bit(KEY_RESERVED, input->keybit);
>  
>  	if (kpad->gpimapsize)
> diff --git a/drivers/input/keyboard/bf54x-keys.c b/drivers/input/keyboard/bf54x-keys.c
> index fc88fb4..96f54e7 100644
> --- a/drivers/input/keyboard/bf54x-keys.c
> +++ b/drivers/input/keyboard/bf54x-keys.c
> @@ -288,8 +288,12 @@ static int bfin_kpad_probe(struct platform_device *pdev)
>  	if (pdata->repeat)
>  		__set_bit(EV_REP, input->evbit);
>  
> -	for (i = 0; i < input->keycodemax; i++)
> -		__set_bit(bf54x_kpad->keycode[i] & KEY_MAX, input->keybit);
> +	for (i = 0; i < input->keycodemax; i++) {
> +		if (bf54x_kpad->keycode[i] > KEY_MAX)
> +			bf54x_kpad->keycode[i] = 0;
> +		else if (bf54x_kpad->keycode[i] > KEY_RESERVED)
> +			__set_bit(bf54x_kpad->keycode[i], input->keybit);
> +	}
>  	__clear_bit(KEY_RESERVED, input->keybit);
>  
>  	error = input_register_device(input);
> diff --git a/drivers/input/misc/pcf8574_keypad.c b/drivers/input/misc/pcf8574_keypad.c
> index e373929..48829f3 100644
> --- a/drivers/input/misc/pcf8574_keypad.c
> +++ b/drivers/input/misc/pcf8574_keypad.c
> @@ -114,8 +114,12 @@ static int pcf8574_kp_probe(struct i2c_client *client, const struct i2c_device_i
>  
>  	for (i = 0; i < ARRAY_SIZE(pcf8574_kp_btncode); i++) {
>  		lp->btncode[i] = pcf8574_kp_btncode[i];
> -		__set_bit(lp->btncode[i] & KEY_MAX, idev->keybit);
> +		if (lp->btncode[i] > KEY_MAX)
> +			lp->btncode[i] = 0;
> +		else if (lp->btncode[i] > KEY_RESERVED)
> +			__set_bit(lp->btncode[i], idev->keybit);
>  	}
> +	__clear_bit(KEY_RESERVED, idev->keybit);
>  
>  	sprintf(lp->name, DRV_NAME);
>  	sprintf(lp->phys, "kp_data/input0");
> diff --git a/drivers/media/pci/ttpci/av7110_ir.c b/drivers/media/pci/ttpci/av7110_ir.c
> index 0e763a7..7fdac45 100644
> --- a/drivers/media/pci/ttpci/av7110_ir.c
> +++ b/drivers/media/pci/ttpci/av7110_ir.c
> @@ -201,6 +201,7 @@ static void input_register_keys(struct infrared *ir)
>  		else if (ir->key_map[i] > KEY_RESERVED)
>  			set_bit(ir->key_map[i], ir->input_dev->keybit);
>  	}
> +	__clear_bit(KEY_RESERVED, ir->input_dev->keybit);

Here we only setting bits for keycodes above KEY_RESERVED so we do not
need to clear KEY_RESERVED bit.

>  
>  	ir->input_dev->keycode = ir->key_map;
>  	ir->input_dev->keycodesize = sizeof(ir->key_map[0]);
> -- 
> 1.7.1
> 

Edited and applied, thank you.

-- 
Dmitry

^ permalink raw reply

* [PATCH] Input: ads7846: Convert to devm_hwmon_device_register_with_groups
From: Guenter Roeck @ 2013-11-23 21:37 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, lm-sensors, linux-kernel, Guenter Roeck

Simplify the code and create mandatory 'name' attribute by using
new hwmon API.

Also use is_visible to determine visible attributes instead of creating
several different attribute groups.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
Compiled tested only.

 drivers/input/touchscreen/ads7846.c |   97 +++++++++--------------------------
 1 file changed, 24 insertions(+), 73 deletions(-)

diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index ea19536..38acc4c 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -101,11 +101,6 @@ struct ads7846 {
 	struct spi_device	*spi;
 	struct regulator	*reg;
 
-#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE)
-	struct attribute_group	*attr_group;
-	struct device		*hwmon;
-#endif
-
 	u16			model;
 	u16			vref_mv;
 	u16			vref_delay_usecs;
@@ -479,41 +474,37 @@ static inline unsigned vbatt_adjust(struct ads7846 *ts, ssize_t v)
 SHOW(in0_input, vaux, vaux_adjust)
 SHOW(in1_input, vbatt, vbatt_adjust)
 
-static struct attribute *ads7846_attributes[] = {
-	&dev_attr_temp0.attr,
-	&dev_attr_temp1.attr,
-	&dev_attr_in0_input.attr,
-	&dev_attr_in1_input.attr,
-	NULL,
-};
-
-static struct attribute_group ads7846_attr_group = {
-	.attrs = ads7846_attributes,
-};
+static umode_t ads7846_is_visible(struct kobject *kobj, struct attribute *attr,
+				  int index)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct ads7846 *ts = dev_get_drvdata(dev);
 
-static struct attribute *ads7843_attributes[] = {
-	&dev_attr_in0_input.attr,
-	&dev_attr_in1_input.attr,
-	NULL,
-};
+	if (ts->model == 7843 && index < 2)	/* in0, in1 */
+		return 0;
+	if (ts->model == 7845 && index != 2)	/* in0 */
+		return 0;
 
-static struct attribute_group ads7843_attr_group = {
-	.attrs = ads7843_attributes,
-};
+	return attr->mode;
+}
 
-static struct attribute *ads7845_attributes[] = {
-	&dev_attr_in0_input.attr,
+static struct attribute *ads7846_attributes[] = {
+	&dev_attr_temp0.attr,		/* 0 */
+	&dev_attr_temp1.attr,		/* 1 */
+	&dev_attr_in0_input.attr,	/* 2 */
+	&dev_attr_in1_input.attr,	/* 3 */
 	NULL,
 };
 
-static struct attribute_group ads7845_attr_group = {
-	.attrs = ads7845_attributes,
+static struct attribute_group ads7846_attr_group = {
+	.attrs = ads7846_attributes,
+	.is_visible = ads7846_is_visible,
 };
+__ATTRIBUTE_GROUPS(ads7846_attr);
 
 static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts)
 {
 	struct device *hwmon;
-	int err;
 
 	/* hwmon sensors need a reference voltage */
 	switch (ts->model) {
@@ -535,56 +526,20 @@ static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts)
 		break;
 	}
 
-	/* different chips have different sensor groups */
-	switch (ts->model) {
-	case 7846:
-		ts->attr_group = &ads7846_attr_group;
-		break;
-	case 7845:
-		ts->attr_group = &ads7845_attr_group;
-		break;
-	case 7843:
-		ts->attr_group = &ads7843_attr_group;
-		break;
-	default:
-		dev_dbg(&spi->dev, "ADS%d not recognized\n", ts->model);
-		return 0;
-	}
-
-	err = sysfs_create_group(&spi->dev.kobj, ts->attr_group);
-	if (err)
-		return err;
-
-	hwmon = hwmon_device_register(&spi->dev);
-	if (IS_ERR(hwmon)) {
-		sysfs_remove_group(&spi->dev.kobj, ts->attr_group);
+	hwmon = devm_hwmon_device_register_with_groups(&spi->dev, spi->modalias,
+						       ts, ads7846_attr_groups);
+	if (IS_ERR(hwmon))
 		return PTR_ERR(hwmon);
-	}
 
-	ts->hwmon = hwmon;
 	return 0;
 }
 
-static void ads784x_hwmon_unregister(struct spi_device *spi,
-				     struct ads7846 *ts)
-{
-	if (ts->hwmon) {
-		sysfs_remove_group(&spi->dev.kobj, ts->attr_group);
-		hwmon_device_unregister(ts->hwmon);
-	}
-}
-
 #else
 static inline int ads784x_hwmon_register(struct spi_device *spi,
 					 struct ads7846 *ts)
 {
 	return 0;
 }
-
-static inline void ads784x_hwmon_unregister(struct spi_device *spi,
-					    struct ads7846 *ts)
-{
-}
 #endif
 
 static ssize_t ads7846_pen_down_show(struct device *dev,
@@ -1447,7 +1402,7 @@ static int ads7846_probe(struct spi_device *spi)
 
 	err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group);
 	if (err)
-		goto err_remove_hwmon;
+		goto err_free_irq;
 
 	err = input_register_device(input_dev);
 	if (err)
@@ -1466,8 +1421,6 @@ static int ads7846_probe(struct spi_device *spi)
 
  err_remove_attr_group:
 	sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group);
- err_remove_hwmon:
-	ads784x_hwmon_unregister(spi, ts);
  err_free_irq:
 	free_irq(spi->irq, ts);
  err_disable_regulator:
@@ -1500,8 +1453,6 @@ static int ads7846_remove(struct spi_device *spi)
 
 	input_unregister_device(ts->input);
 
-	ads784x_hwmon_unregister(spi, ts);
-
 	regulator_disable(ts->reg);
 	regulator_put(ts->reg);
 
-- 
1.7.9.7

^ permalink raw reply related

* Re: [PATCHv7 0/4] twl4030-pwrbutton DT binding
From: Sebastian Reichel @ 2013-11-24 16:33 UTC (permalink / raw)
  To: Dmitry Torokhov, Dmitry Torokhov
  Cc: linux-input-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1383948687-32279-1-git-send-email-sre-8fiUuRrzOP0dnm+yROfE0A@public.gmane.org>

[-- Attachment #1: Type: text/plain, Size: 256 bytes --]

Hi,

On Fri, Nov 08, 2013 at 11:11:23PM +0100, Sebastian Reichel wrote:
> This is the seventh iteration of DT support for the TWL4030
> power button.

Dmitry, can you add this patchset to your queue?

It got no further comments in two weeks.

-- Sebastian

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCHv3 1/2] Input: twl4030-keypad - add device tree support
From: Sebastian Reichel @ 2013-11-24 17:17 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Dmitry Torokhov, linux-input, Rob Herring, Pawel Moll,
	Mark Rutland, Stephen Warren, Ian Campbell, Rob Landley,
	Grant Likely, devicetree, linux-doc, linux-kernel
In-Reply-To: <20131119125031.GA14587@amd.pavel.ucw.cz>

[-- Attachment #1: Type: text/plain, Size: 774 bytes --]

Hi,

> > > I'd suggest just simple "autorepeat", so that we get rid of ugly double-negation.
> > The idea was that majority of setups want autorepeat so in the absence
> > of the property autorepeat is turned on.
> 
> I see... but autorepeat on by default makes sense on devices that are
> usually querty keybaords, but does not make sense on devices that are
> usually phone keypads or power buttons.
> 
> So my proposal is:
> 
> 1) driver decides if it makes sense to autorepeat by default or not.
> 
> 2) dts says autorepeat=0 or autorepeat=1
> 
> That way, we get dts that get chance to work on other OSes, get rid of
> double negations, and get right defaults when autorepeat is not
> specified.

Sounds fine to me. Any objections?

-- Sebastian

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* [PATCH] Input: serio: remove unnecessary pci_set_drvdata()
From: Jingoo Han @ 2013-11-25  2:13 UTC (permalink / raw)
  To: 'Dmitry Torokhov'
  Cc: 'Dmitry Torokhov', linux-input, 'Jingoo Han'

The driver core clears the driver data to NULL after device_release
or on probe failure. Thus, it is not needed to manually clear the
device driver data to NULL.

Signed-off-by: Jingoo Han <jg1.han@samsung.com>
---
 drivers/input/serio/pcips2.c |    1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/input/serio/pcips2.c b/drivers/input/serio/pcips2.c
index 76f8383..13062f6 100644
--- a/drivers/input/serio/pcips2.c
+++ b/drivers/input/serio/pcips2.c
@@ -181,7 +181,6 @@ static void pcips2_remove(struct pci_dev *dev)
 	struct pcips2_data *ps2if = pci_get_drvdata(dev);
 
 	serio_unregister_port(ps2if->io);
-	pci_set_drvdata(dev, NULL);
 	kfree(ps2if);
 	pci_release_regions(dev);
 	pci_disable_device(dev);
-- 
1.7.10.4



^ permalink raw reply related

* Re: [PATCHv9][ 1/3] Input: tsc2007: Add device tree support.
From: Denis Carikli @ 2013-11-25  8:39 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Mark Rutland, devicetree, linux-fbdev, Eric Bénard,
	Pawel Moll, Stephen Warren, Ian Campbell, Rob Herring,
	Tomi Valkeinen, Sascha Hauer, linux-input, Grant Likely,
	Shawn Guo, Jean-Christophe Plagniol-Villard, linux-arm-kernel,
	Lothar Waßmann
In-Reply-To: <20131119211102.GA14888@core.coreip.homeip.net>

On 11/19/2013 10:11 PM, Dmitry Torokhov wrote:
 > Input: tsc2007 misc fixes
 >
 > From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
 >
 > Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

Tested-by: Denis Carikli <denis@eukrea.com>

Denis.

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox