Linux Input/HID development
 help / color / mirror / Atom feed
* [PATCH 2/2] HID: wacom - Update maximum X/Y accounding to outbound offset
From: Ping Cheng @ 2014-12-05  2:25 UTC (permalink / raw)
  To: jkosina; +Cc: linux-input, Ping Cheng

Defined outbound offset for DTU and Cintiq. But didn't update
the relevant maximum values. Oops...

Signed-off-by: Ping Cheng <pingc@wacom.com>
---
 drivers/hid/wacom_wac.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index b65e0d7..7b7a61a 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -2685,11 +2685,11 @@ static const struct wacom_features wacom_features_0x317 =
 	  INTUOSPL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16,
 	  .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
 static const struct wacom_features wacom_features_0xF4 =
-	{ "Wacom Cintiq 24HD", 104280, 65400, 2047, 63,
+	{ "Wacom Cintiq 24HD", 104080, 65200, 2047, 63,
 	  WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
 	  WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0xF8 =
-	{ "Wacom Cintiq 24HD touch", 104280, 65400, 2047, 63, /* Pen */
+	{ "Wacom Cintiq 24HD touch", 104080, 65200, 2047, 63, /* Pen */
 	  WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
 	  WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
 	  .oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf6 };
@@ -2707,7 +2707,7 @@ static const struct wacom_features wacom_features_0xC6 =
 	{ "Wacom Cintiq 12WX", 53020, 33440, 1023, 63,
 	  WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
 static const struct wacom_features wacom_features_0x304 =
-	{ "Wacom Cintiq 13HD", 59352, 33648, 1023, 63,
+	{ "Wacom Cintiq 13HD", 59152, 33448, 1023, 63,
 	  WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
 	  WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0xC7 =
@@ -2721,7 +2721,7 @@ static const struct wacom_features wacom_features_0xF0 =
 	{ "Wacom DTU1631", 34623, 19553, 511, 0,
 	  DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0xFB =
-	{ "Wacom DTU1031", 22096, 13960, 511, 0,
+	{ "Wacom DTU1031", 21896, 13760, 511, 0,
 	  DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
 	  WACOM_DTU_OFFSET, WACOM_DTU_OFFSET };
 static const struct wacom_features wacom_features_0x32F =
@@ -2742,15 +2742,15 @@ static const struct wacom_features wacom_features_0x5D = /* Touch */
 	  .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x59, .touch_max = 10,
 	  .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
 static const struct wacom_features wacom_features_0xCC =
-	{ "Wacom Cintiq 21UX2", 87000, 65400, 2047, 63,
+	{ "Wacom Cintiq 21UX2", 86800, 65200, 2047, 63,
 	  WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
 	  WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0xFA =
-	{ "Wacom Cintiq 22HD", 95640, 54060, 2047, 63,
+	{ "Wacom Cintiq 22HD", 95440, 53860, 2047, 63,
 	  WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
 	  WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0x5B =
-	{ "Wacom Cintiq 22HDT", 95640, 54060, 2047, 63,
+	{ "Wacom Cintiq 22HDT", 95440, 53860, 2047, 63,
 	  WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
 	  WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
 	  .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5e };
@@ -2897,7 +2897,7 @@ static const struct wacom_features wacom_features_0x6004 =
 	{ "ISD-V4", 12800, 8000, 255, 0,
 	  TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0x307 =
-	{ "Wacom ISDv5 307", 59352, 33648, 2047, 63,
+	{ "Wacom ISDv5 307", 59152, 33448, 2047, 63,
 	  CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
 	  WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
 	  .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x309 };
@@ -2906,7 +2906,7 @@ static const struct wacom_features wacom_features_0x309 =
 	  .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x0307, .touch_max = 10,
 	  .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
 static const struct wacom_features wacom_features_0x30A =
-	{ "Wacom ISDv5 30A", 59352, 33648, 2047, 63,
+	{ "Wacom ISDv5 30A", 59152, 33448, 2047, 63,
 	  CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
 	  WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
 	  .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x30C };
-- 
1.9.1


^ permalink raw reply related

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
From: jeffrey.lin @ 2014-12-05  8:15 UTC (permalink / raw)
  To: dmitry.torokhov, shc_work, bleung, lee.jones, charliemooney
  Cc: KP.li, linux-kernel, linux-input, jeffrey.lin

From: "jeffrey.lin" <jeffrey.lin@rad-ic.com>

This patch is porting Raydium I2C touch driver. Developer can enable raydium touch driver by modifying define
"CONFIG_TOUCHSCREEN_RM_TS".

Signed-off-by: jeffrey.lin@rad-ic.com
---
 drivers/input/touchscreen/Kconfig  | 12 ++++++++++++
 drivers/input/touchscreen/Makefile |  1 +
 2 files changed, 13 insertions(+)

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3ce9181..d0324d2 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -955,4 +955,16 @@ config TOUCHSCREEN_ZFORCE
 	  To compile this driver as a module, choose M here: the
 	  module will be called zforce_ts.
 
+config TOUCHSCREEN_RM_TS
+	tristate "Raydium I2C Touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have Raydium series I2C touchscreen,
+	  such as RM31100 , connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rm31100_ts.
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 687d5a7..3220f66 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -78,3 +78,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+= zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)	+= tps6507x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= rm31100_ts.o
-- 
2.1.2


^ permalink raw reply related

* [PATCH v4] input: Add ROHM BU21023/24 Dual touch support resistive touchscreens
From: Yoichi Yuasa @ 2014-12-05  9:17 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: yuasa, linux-input

v4 Changes:
- remove inline
- master_xfer checks in probe()
- rewrite rohm_i2c_burst_read()
- rohm_i2c_burst_read() transfer error returns -EIO
- remove unused module parameters
- fix prev_touch_report update
- pass NULL to hard_irq
- per-device parameters use sysfs
- fix errno
- header file is taken in .c

v3 Changes:
- fix multi touch slots initialization
- fix set_bit BTN_TOUCH
- remove input_unregister_device()
- switch to __set_bit()

v2 Changes:
- remove polling mode
- switch to threaded interrupt
- switch to managed resources
- use MT-B protocol with input_mt_assign_slots
- provide ST emulation
- firmware load and device initialization are shifted at opening the
  device
- add error handling for IO operations

Signed-off-by: Yoichi Yuasa <yuasa@linux-mips.org>
---
 drivers/input/touchscreen/Kconfig        |   11 +
 drivers/input/touchscreen/Makefile       |    1 +
 drivers/input/touchscreen/rohm_bu21023.c | 1257 ++++++++++++++++++++++++++++++
 3 files changed, 1269 insertions(+)
 create mode 100644 drivers/input/touchscreen/rohm_bu21023.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 568a020..80811a7 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -975,4 +975,15 @@ config TOUCHSCREEN_ZFORCE
 	  To compile this driver as a module, choose M here: the
 	  module will be called zforce_ts.
 
+config TOUCHSCREEN_ROHM_BU21023
+	tristate "ROHM BU21023/24 Dual touch support resistive touchscreens"
+	depends on I2C
+	help
+	  Say Y here if you have a touchscreen using ROHM BU21023/24.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called bu21023_ts.
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index dab4a56..5c2917b 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)	+= usbtouchscreen.o
 obj-$(CONFIG_TOUCHSCREEN_PCAP)		+= pcap_ts.o
 obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
 obj-$(CONFIG_TOUCHSCREEN_PIXCIR)	+= pixcir_i2c_ts.o
+obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023)	+= rohm_bu21023.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
diff --git a/drivers/input/touchscreen/rohm_bu21023.c b/drivers/input/touchscreen/rohm_bu21023.c
new file mode 100644
index 0000000..4c460c6
--- /dev/null
+++ b/drivers/input/touchscreen/rohm_bu21023.c
@@ -0,0 +1,1257 @@
+/*
+ * ROHM BU21023/24 Dual touch support resistive touch screen driver
+ * Copyright (C) 2012 ROHM CO.,LTD.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/hrtimer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#define BU21023_NAME		"bu21023_ts"
+#define BU21023_FIRMWARE_NAME	"bu21023.bin"
+
+#define BU21023_MAX_SLOTS	2
+
+#define AXIS_ADJUST		4
+#define AXIS_OFFSET		8
+
+#define FIRMWARE_BLOCK_SIZE	32
+#define FIRMWARE_RETRY_MAX	4
+
+#define SAMPLING_DELAY		12	/* msec */
+
+#define CALIBRATION_RETRY_MAX	6
+
+#define ROHM_TS_ABS_X_MIN	40
+#define ROHM_TS_ABS_X_MAX	990
+#define ROHM_TS_ABS_Y_MIN	160
+#define ROHM_TS_ABS_Y_MAX	920
+
+/*
+ * BU21023GUL/BU21023MUV/BU21024FV-M registers map
+ */
+#define VADOUT_YP_H		0x00
+#define VADOUT_YP_L		0x01
+#define VADOUT_XP_H		0x02
+#define VADOUT_XP_L		0x03
+#define VADOUT_YN_H		0x04
+#define VADOUT_YN_L		0x05
+#define VADOUT_XN_H		0x06
+#define VADOUT_XN_L		0x07
+
+#define PRM1_X_H		0x08
+#define PRM1_X_L		0x09
+#define PRM1_Y_H		0x0a
+#define PRM1_Y_L		0x0b
+#define PRM2_X_H		0x0c
+#define PRM2_X_L		0x0d
+#define PRM2_Y_H		0x0e
+#define PRM2_Y_L		0x0f
+
+#define MLT_PRM_MONI_X		0x10
+#define MLT_PRM_MONI_Y		0x11
+
+#define DEBUG_MONI_1		0x12
+#define DEBUG_MONI_2		0x13
+
+#define VADOUT_ZX_H		0x14
+#define VADOUT_ZX_L		0x15
+#define VADOUT_ZY_H		0x16
+#define VADOUT_ZY_L		0x17
+
+#define Z_PARAM_H		0x18
+#define Z_PARAM_L		0x19
+
+/*
+ * Value for VADOUT_*_L
+ */
+#define VADOUT_L_MASK		0x01
+
+/*
+ * Value for PRM*_*_L
+ */
+#define PRM_L_MASK		0x01
+
+#define POS_X1_H		0x20
+#define POS_X1_L		0x21
+#define POS_Y1_H		0x22
+#define POS_Y1_L		0x23
+#define POS_X2_H		0x24
+#define POS_X2_L		0x25
+#define POS_Y2_H		0x26
+#define POS_Y2_L		0x27
+
+/*
+ * Value for POS_*_L
+ */
+#define POS_L_MASK		0x01
+
+#define TOUCH			0x28
+#define TOUCH_DETECT		0x01
+
+#define TOUCH_GESTURE		0x29
+#define SINGLE_TOUCH		0x01
+#define DUAL_TOUCH		0x03
+#define TOUCH_MASK		0x03
+#define CALIBRATION_REQUEST	0x04
+#define CALIBRATION_STATUS	0x08
+#define CALIBRATION_MASK	0x0c
+#define GESTURE_SPREAD		0x10
+#define GESTURE_PINCH		0x20
+#define GESTURE_ROTATE_R	0x40
+#define GESTURE_ROTATE_L	0x80
+
+#define INT_STATUS		0x2a
+#define INT_MASK		0x3d
+#define INT_CLEAR		0x3e
+
+/*
+ * Values for INT_*
+ */
+#define COORD_UPDATE		0x01
+#define CALIBRATION_DONE	0x02
+#define SLEEP_IN		0x04
+#define SLEEP_OUT		0x08
+#define PROGRAM_LOAD_DONE	0x10
+#define ERROR			0x80
+#define INT_ALL			0x9f
+
+#define ERR_STATUS		0x2b
+#define ERR_MASK		0x3f
+
+/*
+ * Values for ERR_*
+ */
+#define ADC_TIMEOUT		0x01
+#define CPU_TIMEOUT		0x02
+#define CALIBRATION_ERR		0x04
+#define PROGRAM_LOAD_ERR	0x10
+
+#define COMMON_SETUP1			0x30
+#define PROGRAM_LOAD_HOST		0x02
+#define PROGRAM_LOAD_EEPROM		0x03
+#define CENSOR_4PORT			0x04
+#define CENSOR_8PORT			0x00	/* Not supported by BU21023 */
+#define CALIBRATION_TYPE_DEFAULT	0x08
+#define CALIBRATION_TYPE_SPECIAL	0x00
+#define INT_ACTIVE_HIGH			0x10
+#define INT_ACTIVE_LOW			0x00
+#define AUTO_CALIBRATION		0x40
+#define MANUAL_CALIBRATION		0x00
+#define COMMON_SETUP1_DEFAULT		0x4e
+
+#define COMMON_SETUP2		0x31
+#define MAF_NONE		0x00
+#define MAF_1SAMPLE		0x01
+#define MAF_3SAMPLES		0x02
+#define MAF_5SAMPLES		0x03
+#define INV_Y			0x04
+#define INV_X			0x08
+#define SWAP_XY			0x10
+
+#define COMMON_SETUP3		0x32
+#define EN_SLEEP		0x01
+#define EN_MULTI		0x02
+#define EN_GESTURE		0x04
+#define EN_INTVL		0x08
+#define SEL_STEP		0x10
+#define SEL_MULTI		0x20
+#define SEL_TBL_DEFAULT		0x40
+
+#define INTERVAL_TIME		0x33
+#define INTERVAL_TIME_DEFAULT	0x10
+
+#define STEP_X			0x34
+#define STEP_X_DEFAULT		0x41
+
+#define STEP_Y			0x35
+#define STEP_Y_DEFAULT		0x8d
+
+#define OFFSET_X		0x38
+#define OFFSET_X_DEFAULT	0x0c
+
+#define OFFSET_Y		0x39
+#define OFFSET_Y_DEFAULT	0x0c
+
+#define THRESHOLD_TOUCH		0x3a
+#define THRESHOLD_TOUCH_DEFAULT	0xa0
+
+#define THRESHOLD_GESTURE		0x3b
+#define THRESHOLD_GESTURE_DEFAULT	0x17
+
+#define SYSTEM			0x40
+#define ANALOG_POWER_ON		0x01
+#define ANALOG_POWER_OFF	0x00
+#define CPU_POWER_ON		0x02
+#define CPU_POWER_OFF		0x00
+
+#define FORCE_CALIBRATION	0x42
+#define FORCE_CALIBRATION_ON	0x01
+#define FORCE_CALIBRATION_OFF	0x00
+
+#define CPU_FREQ		0x50	/* 10 / (reg + 1) MHz */
+#define CPU_FREQ_10MHZ		0x00
+#define CPU_FREQ_5MHZ		0x01
+#define CPU_FREQ_1MHZ		0x09
+
+#define EEPROM_ADDR		0x51
+
+#define CALIBRATION_ADJUST		0x52
+#define CALIBRATION_ADJUST_DEFAULT	0x00
+
+#define THRESHOLD_SLEEP_IN	0x53
+
+#define EVR_XY			0x56
+#define EVR_XY_DEFAULT		0x10
+
+#define PRM_SWOFF_TIME		0x57
+#define PRM_SWOFF_TIME_DEFAULT	0x04
+
+#define PROGRAM_VERSION		0x5f
+
+#define ADC_CTRL		0x60
+#define ADC_DIV_MASK		0x1f	/* The minimum value is 4 */
+#define ADC_DIV_DEFAULT		0x08
+
+#define ADC_WAIT		0x61
+#define ADC_WAIT_DEFAULT	0x0a
+
+#define SWCONT			0x62
+#define SWCONT_DEFAULT		0x0f
+
+#define EVR_X			0x63
+#define EVR_X_DEFAULT		0x86
+
+#define EVR_Y			0x64
+#define EVR_Y_DEFAULT		0x64
+
+#define TEST1			0x65
+#define DUALTOUCH_STABILIZE_ON	0x01
+#define DUALTOUCH_STABILIZE_OFF	0x00
+#define DUALTOUCH_REG_ON	0x20
+#define DUALTOUCH_REG_OFF	0x00
+
+#define CALIBRATION_REG1		0x68
+#define CALIBRATION_REG1_DEFAULT	0xd9
+
+#define CALIBRATION_REG2		0x69
+#define CALIBRATION_REG2_DEFAULT	0x36
+
+#define CALIBRATION_REG3		0x6a
+#define CALIBRATION_REG3_DEFAULT	0x32
+
+#define EX_ADDR_H		0x70
+#define EX_ADDR_L		0x71
+#define EX_WDAT			0x72
+#define EX_RDAT			0x73
+#define EX_CHK_SUM1		0x74
+#define EX_CHK_SUM2		0x75
+#define EX_CHK_SUM3		0x76
+
+struct rohm_ts_data {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+
+	bool initialized;
+
+	unsigned int untouch_count;
+	unsigned int single_touch_count;
+	unsigned int dual_touch_count;
+	unsigned int prev_touch_report;
+
+	u8 setup2;
+};
+
+/*
+ * rohm_i2c_burst_read - execute combined I2C message for ROHM BU21023/24
+ * @client: Handle to ROHM BU21023/24
+ * @start: Where to start read address from ROHM BU21023/24
+ * @buf: Where to store read data from ROHM BU21023/24
+ * @len: How many bytes to read
+ *
+ * Returns negative errno, else zero on success.
+ *
+ * Note
+ * In BU21023/24 burst read, stop condition is needed after "address write".
+ * Therefore, transmission is performed in 2 steps.
+ */
+static int rohm_i2c_burst_read(struct i2c_client *client, u8 start, void *buf,
+			       size_t len)
+{
+	struct i2c_adapter *adap = client->adapter;
+	struct i2c_msg msg[2];
+	int i, ret = 0;
+
+	msg[0].addr = client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 1;
+	msg[0].buf = &start;
+
+	msg[1].addr = client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = len;
+	msg[1].buf = buf;
+
+	i2c_lock_adapter(adap);
+
+	for (i = 0; i < 2; i++) {
+		if (__i2c_transfer(adap, &msg[i], 1) < 0) {
+			ret = -EIO;
+			break;
+		}
+	}
+
+	i2c_unlock_adapter(adap);
+
+	return ret;
+}
+
+static int rohm_ts_manual_calibration(struct rohm_ts_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	struct device *dev = &client->dev;
+	u8 buf[33];
+
+	int retry;
+	bool success = false;
+	bool first_time = true;
+	bool calibration_done;
+
+	u8 reg1, reg2, reg3;
+	s32 reg1_orig, reg2_orig, reg3_orig;
+	s32 val;
+
+	int calib_x = 0, calib_y = 0;
+	int reg_x, reg_y;
+	int err_x, err_y;
+
+	int err = 0, ret;
+	int i;
+
+	reg1_orig = i2c_smbus_read_byte_data(client, CALIBRATION_REG1);
+	if (reg1_orig < 0)
+		return reg1_orig;
+
+	reg2_orig = i2c_smbus_read_byte_data(client, CALIBRATION_REG2);
+	if (reg2_orig < 0)
+		return reg2_orig;
+
+	reg3_orig = i2c_smbus_read_byte_data(client, CALIBRATION_REG3);
+	if (reg3_orig < 0)
+		return reg3_orig;
+
+	ret = i2c_smbus_write_byte_data(client, INT_MASK,
+					COORD_UPDATE | SLEEP_IN | SLEEP_OUT |
+					PROGRAM_LOAD_DONE);
+	if (ret) {
+		err = ret;
+		goto err_exit;
+	}
+
+	ret = i2c_smbus_write_byte_data(client, TEST1, DUALTOUCH_STABILIZE_ON);
+	if (ret) {
+		err = ret;
+		goto err_exit;
+	}
+
+	for (retry = 0; retry < CALIBRATION_RETRY_MAX; retry++) {
+		/* wait 2 sampling for update */
+		mdelay(2 * SAMPLING_DELAY);
+
+#define READ_CALIB_BUF(reg)	((u16)buf[((reg) - PRM1_X_H)])
+
+		ret = rohm_i2c_burst_read(client, PRM1_X_H, buf, sizeof(buf));
+		if (ret < 0) {
+			err = ret;
+			goto err_exit;
+		}
+
+		if (READ_CALIB_BUF(TOUCH) & TOUCH_DETECT)
+			continue;
+
+		if (first_time) {
+			/* generate calibration parameter */
+			calib_x =
+			    (READ_CALIB_BUF(PRM1_X_H) << 2 |
+			     READ_CALIB_BUF(PRM1_X_L)) - AXIS_OFFSET;
+			calib_y =
+			    (READ_CALIB_BUF(PRM1_Y_H) << 2 |
+			     READ_CALIB_BUF(PRM1_Y_L)) - AXIS_OFFSET;
+
+			ret = i2c_smbus_write_byte_data(client, TEST1,
+							DUALTOUCH_STABILIZE_ON |
+							DUALTOUCH_REG_ON);
+			if (ret) {
+				err = ret;
+				goto err_exit;
+			}
+
+			first_time = false;
+		} else {
+			/* generate adjustment parameter */
+			err_x = READ_CALIB_BUF(PRM1_X_H) << 2 |
+			    READ_CALIB_BUF(PRM1_X_L);
+			err_y = READ_CALIB_BUF(PRM1_Y_H) << 2 |
+			    READ_CALIB_BUF(PRM1_Y_L);
+
+			/* X axis ajust */
+			if (err_x <= 4)
+				calib_x -= AXIS_ADJUST;
+			else if (err_x >= 60)
+				calib_x += AXIS_ADJUST;
+
+			/* Y axis ajust */
+			if (err_y <= 4)
+				calib_y -= AXIS_ADJUST;
+			else if (err_y >= 60)
+				calib_y += AXIS_ADJUST;
+		}
+
+		/* generate calibration setting value */
+		reg_x = calib_x + ((calib_x & 0x200) << 1);
+		reg_y = calib_y + ((calib_y & 0x200) << 1);
+
+		/* convert for register format */
+		reg1 = reg_x >> 3;
+		reg2 = (reg_y & 0x7) << 4 | (reg_x & 0x7);
+		reg3 = reg_y >> 3;
+
+		ret = i2c_smbus_write_byte_data(client, CALIBRATION_REG1, reg1);
+		if (ret) {
+			err = ret;
+			goto err_exit;
+		}
+
+		ret = i2c_smbus_write_byte_data(client, CALIBRATION_REG2, reg2);
+		if (ret) {
+			err = ret;
+			goto err_exit;
+		}
+
+		ret = i2c_smbus_write_byte_data(client, CALIBRATION_REG3, reg3);
+		if (ret) {
+			err = ret;
+			goto err_exit;
+		}
+
+		/*
+		 * force calibration sequcence
+		 */
+		ret = i2c_smbus_write_byte_data(client, FORCE_CALIBRATION,
+						FORCE_CALIBRATION_OFF);
+		if (ret) {
+			err = ret;
+			goto err_exit;
+		}
+
+		ret = i2c_smbus_write_byte_data(client, FORCE_CALIBRATION,
+						FORCE_CALIBRATION_ON);
+		if (ret) {
+			err = ret;
+			goto err_exit;
+		}
+
+		/* clear all interrupts */
+		ret = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+		if (ret) {
+			err = ret;
+			goto err_exit;
+		}
+
+		/*
+		 * Wait for the status change of calibration, max 10 sampling
+		 */
+		calibration_done = false;
+
+		for (i = 0; i < 10; i++) {
+			mdelay(SAMPLING_DELAY);
+
+			val = i2c_smbus_read_byte_data(client, TOUCH_GESTURE);
+			if (!(val & CALIBRATION_MASK)) {
+				calibration_done = true;
+				break;
+			} else if (val < 0) {
+				err = val;
+				goto err_exit;
+			}
+		}
+
+		if (calibration_done) {
+			val = i2c_smbus_read_byte_data(client, INT_STATUS);
+			if (val == CALIBRATION_DONE) {
+				success = true;
+				break;
+			} else if (val < 0) {
+				err = val;
+				goto err_exit;
+			}
+		} else {
+			dev_warn(dev, "Calibration timeout\n");
+		}
+	}
+
+	if (!success) {
+		ret = i2c_smbus_write_byte_data(client, CALIBRATION_REG1,
+						reg1_orig);
+		if (ret) {
+			err = ret;
+			goto err_exit;
+		}
+
+		ret = i2c_smbus_write_byte_data(client, CALIBRATION_REG2,
+						reg2_orig);
+		if (ret) {
+			err = ret;
+			goto err_exit;
+		}
+
+		ret = i2c_smbus_write_byte_data(client, CALIBRATION_REG3,
+						reg3_orig);
+		if (ret) {
+			err = ret;
+			goto err_exit;
+		}
+
+		/* calibration data enable */
+		ret = i2c_smbus_write_byte_data(client, TEST1,
+						DUALTOUCH_STABILIZE_ON |
+						DUALTOUCH_REG_ON);
+		if (ret) {
+			err = ret;
+			goto err_exit;
+		}
+
+		/* wait 10 sampling */
+		mdelay(10 * SAMPLING_DELAY);
+
+		err = -EBUSY;
+	}
+
+err_exit:
+	ret = i2c_smbus_write_byte_data(client, INT_MASK, INT_ALL);
+	if (!ret)
+		/* Clear all interrupts */
+		ret = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+
+	if (!err && ret)
+		err = ret;
+
+	return err;
+}
+
+static unsigned int untouch_threshold[3] = { 0, 1, 5 };
+static unsigned int single_touch_threshold[3] = { 0, 0, 4 };
+static unsigned int dual_touch_threshold[3] = { 10, 8, 0 };
+
+module_param_array(untouch_threshold, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(single_touch_threshold, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(dual_touch_threshold, uint, NULL, S_IRUGO | S_IWUSR);
+
+MODULE_PARM_DESC(untouch_threshold, "Thresholds for un-touch detection");
+MODULE_PARM_DESC(single_touch_threshold,
+		 "Thresholds for single touch detection");
+MODULE_PARM_DESC(dual_touch_threshold, "Thresholds for dual touch detection");
+
+static irqreturn_t rohm_ts_soft_irq(int irq, void *dev_id)
+{
+	struct rohm_ts_data *ts = dev_id;
+	struct i2c_client *client = ts->client;
+	struct input_dev *input_dev = ts->input_dev;
+	struct device *dev = &client->dev;
+
+	u8 buf[10];		/* for 0x20-0x29 */
+
+	struct input_mt_pos pos[BU21023_MAX_SLOTS];
+	int slots[BU21023_MAX_SLOTS];
+	u8 touch_flags;
+	unsigned int threshold;
+	int touch_report = -1;
+	unsigned int prev_touch_report = ts->prev_touch_report;
+
+	s32 status;
+	int i, ret;
+
+	status = i2c_smbus_read_byte_data(client, INT_STATUS);
+	if (!status)
+		return IRQ_NONE;
+
+	if (status < 0)
+		return IRQ_HANDLED;
+
+	ret = i2c_smbus_write_byte_data(client, INT_MASK, INT_ALL);
+	if (ret)
+		return IRQ_HANDLED;
+
+	/* Clear all interrupts */
+	ret = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+	if (ret)
+		return IRQ_HANDLED;
+
+#define READ_POS_BUF(reg)	((u16)buf[((reg) - POS_X1_H)])
+
+	ret = rohm_i2c_burst_read(client, POS_X1_H, buf, sizeof(buf));
+	if (ret < 0)
+		return IRQ_HANDLED;
+
+	touch_flags = READ_POS_BUF(TOUCH_GESTURE) & TOUCH_MASK;
+	if (touch_flags) {
+		/* generate coordinates */
+		pos[0].x = (READ_POS_BUF(POS_X1_H) << 2) |
+			   READ_POS_BUF(POS_X1_L);
+		pos[0].y = (READ_POS_BUF(POS_Y1_H) << 2) |
+			   READ_POS_BUF(POS_Y1_L);
+		pos[1].x = (READ_POS_BUF(POS_X2_H) << 2) |
+			   READ_POS_BUF(POS_X2_L);
+		pos[1].y = (READ_POS_BUF(POS_Y2_H) << 2) |
+			   READ_POS_BUF(POS_Y2_L);
+
+		switch (touch_flags) {
+		case SINGLE_TOUCH:
+			ts->untouch_count = 0;
+			ts->single_touch_count++;
+			ts->dual_touch_count = 0;
+
+			threshold = single_touch_threshold[prev_touch_report];
+			if (ts->single_touch_count > threshold)
+				touch_report = 1;
+
+			if (touch_report == 1) {
+				if (pos[1].x != 0 && pos[1].y != 0) {
+					pos[0].x = pos[1].x;
+					pos[0].y = pos[1].y;
+					pos[1].x = 0;
+					pos[1].y = 0;
+				}
+			}
+			break;
+		case DUAL_TOUCH:
+			ts->untouch_count = 0;
+			ts->single_touch_count = 0;
+			ts->dual_touch_count++;
+
+			threshold = dual_touch_threshold[prev_touch_report];
+			if (ts->dual_touch_count > threshold)
+				touch_report = 2;
+			break;
+		default:
+			dev_dbg(dev,
+				"Three or more touches are not supported\n");
+			return IRQ_HANDLED;
+		}
+	} else {
+		ts->untouch_count++;
+
+		threshold = untouch_threshold[prev_touch_report];
+		if (ts->untouch_count > threshold) {
+			ts->untouch_count = 0;
+			ts->single_touch_count = 0;
+			ts->dual_touch_count = 0;
+
+			touch_report = 0;
+		}
+	}
+
+	if (touch_report >= 0) {
+		input_mt_assign_slots(input_dev, slots, pos, touch_report);
+
+		for (i = 0; i < touch_report; i++) {
+			input_mt_slot(input_dev, slots[i]);
+			input_mt_report_slot_state(input_dev,
+						   MT_TOOL_FINGER, true);
+			input_report_abs(input_dev,
+					 ABS_MT_POSITION_X, pos[i].x);
+			input_report_abs(input_dev,
+					 ABS_MT_POSITION_Y, pos[i].y);
+		}
+
+		input_mt_sync_frame(input_dev);
+		input_mt_report_pointer_emulation(input_dev, true);
+		input_sync(input_dev);
+
+		ts->prev_touch_report = touch_report;
+	}
+
+	if (READ_POS_BUF(TOUCH_GESTURE) & CALIBRATION_REQUEST) {
+		if (rohm_ts_manual_calibration(ts) < 0)
+			dev_warn(dev, "Failed to manual calibration\n");
+	}
+
+	i2c_smbus_write_byte_data(client, INT_MASK,
+				  CALIBRATION_DONE | SLEEP_OUT | SLEEP_IN |
+				  PROGRAM_LOAD_DONE);
+
+	return IRQ_HANDLED;
+}
+
+static int rohm_ts_load_firmware(struct i2c_client *client,
+				 const char *firmware_name)
+{
+	struct device *dev = &client->dev;
+	const struct firmware *firmware = NULL;
+	s32 status;
+	int blocks, remainder, retry = 0, offset;
+	int err = 0, ret;
+	int i;
+
+	ret = request_firmware(&firmware, firmware_name, dev);
+	if (ret) {
+		dev_err(dev, "Unable to open firmware %s\n", firmware_name);
+		return ret;
+	}
+
+	blocks = firmware->size / FIRMWARE_BLOCK_SIZE;
+	remainder = firmware->size % FIRMWARE_BLOCK_SIZE;
+
+	ret = i2c_smbus_write_byte_data(client, INT_MASK,
+					COORD_UPDATE | CALIBRATION_DONE |
+					SLEEP_IN | SLEEP_OUT);
+	if (ret) {
+		err = ret;
+		goto err_int_mask_exit;
+	}
+
+	while (retry < FIRMWARE_RETRY_MAX) {
+		ret = i2c_smbus_write_byte_data(client, COMMON_SETUP1,
+						COMMON_SETUP1_DEFAULT);
+		if (ret) {
+			err = ret;
+			goto err_int_mask_exit;
+		}
+
+		ret = i2c_smbus_write_byte_data(client, EX_ADDR_H, 0);
+		if (ret) {
+			err = ret;
+			goto err_int_mask_exit;
+		}
+
+		ret = i2c_smbus_write_byte_data(client, EX_ADDR_L, 0);
+		if (ret) {
+			err = ret;
+			goto err_int_mask_exit;
+		}
+
+		offset = 0;
+
+		/* firmware load to the device */
+		for (i = 0; i < blocks; i++) {
+			ret = i2c_smbus_write_i2c_block_data(client, EX_WDAT,
+				FIRMWARE_BLOCK_SIZE, &firmware->data[offset]);
+			if (ret) {
+				err = ret;
+				goto err_int_mask_exit;
+			}
+
+			offset += FIRMWARE_BLOCK_SIZE;
+		}
+
+		if (remainder) {
+			ret = i2c_smbus_write_i2c_block_data(client, EX_WDAT,
+				remainder, &firmware->data[offset]);
+			if (ret) {
+				err = ret;
+				goto err_int_mask_exit;
+			}
+		}
+
+		/* check formware load result */
+		status = i2c_smbus_read_byte_data(client, INT_STATUS);
+		if (status < 0) {
+			err = status;
+			goto err_int_mask_exit;
+		}
+
+		/* clear all interrupts */
+		ret = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+		if (ret) {
+			err = ret;
+			goto err_int_mask_exit;
+		}
+
+		if (status == PROGRAM_LOAD_DONE)
+			break;
+
+		/* settings for retry */
+		ret = i2c_smbus_write_byte_data(client, EX_WDAT, 0);
+		if (ret) {
+			err = ret;
+			goto err_int_mask_exit;
+		}
+
+		retry++;
+		dev_warn(dev, "Retry firmware load\n");
+	}
+
+err_int_mask_exit:
+	ret = i2c_smbus_write_byte_data(client, INT_MASK, INT_ALL);
+	if (ret)
+		err = ret;
+
+	release_firmware(firmware);
+
+	if (retry >= FIRMWARE_RETRY_MAX)
+		return -EBUSY;
+
+	return err;
+}
+
+static ssize_t swap_xy_show(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct input_dev *input_dev = to_input_dev(dev);
+	struct rohm_ts_data *ts;
+
+	ts = input_get_drvdata(input_dev);
+
+	return sprintf(buf, "%d\n", !!(ts->setup2 & SWAP_XY));
+}
+
+static ssize_t swap_xy_store(struct device *dev, struct device_attribute *attr,
+			     const char *buf, size_t count)
+{
+	struct input_dev *input_dev = to_input_dev(dev);
+	struct rohm_ts_data *ts;
+	unsigned int val;
+	int err = 0;
+
+	ts = input_get_drvdata(input_dev);
+
+	err = kstrtouint(buf, 0, &val);
+	if (err)
+		return err;
+
+	if (val)
+		ts->setup2 |= SWAP_XY;
+	else
+		ts->setup2 &= ~SWAP_XY;
+
+	if (ts->initialized)
+		err = i2c_smbus_write_byte_data(ts->client, COMMON_SETUP2,
+						ts->setup2);
+
+	return err;
+}
+
+static ssize_t inv_x_show(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct input_dev *input_dev = to_input_dev(dev);
+	struct rohm_ts_data *ts;
+
+	ts = input_get_drvdata(input_dev);
+
+	return sprintf(buf, "%d\n", !!(ts->setup2 & INV_X));
+}
+
+static ssize_t inv_x_store(struct device *dev, struct device_attribute *attr,
+			   const char *buf, size_t count)
+{
+	struct input_dev *input_dev = to_input_dev(dev);
+	struct rohm_ts_data *ts;
+	unsigned int val;
+	int err = 0;
+
+	ts = input_get_drvdata(input_dev);
+
+	err = kstrtouint(buf, 0, &val);
+	if (err)
+		return err;
+
+	if (val)
+		ts->setup2 |= INV_X;
+	else
+		ts->setup2 &= ~INV_X;
+
+	if (ts->initialized)
+		err = i2c_smbus_write_byte_data(ts->client, COMMON_SETUP2,
+						ts->setup2);
+
+	return err;
+}
+
+static ssize_t inv_y_show(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct input_dev *input_dev = to_input_dev(dev);
+	struct rohm_ts_data *ts;
+
+	ts = input_get_drvdata(input_dev);
+
+	return sprintf(buf, "%d\n", !!(ts->setup2 & INV_Y));
+}
+
+static ssize_t inv_y_store(struct device *dev, struct device_attribute *attr,
+			   const char *buf, size_t count)
+{
+	struct input_dev *input_dev = to_input_dev(dev);
+	struct rohm_ts_data *ts;
+	unsigned int val;
+	int err = 0;
+
+	ts = input_get_drvdata(input_dev);
+
+	err = kstrtouint(buf, 0, &val);
+	if (err)
+		return err;
+
+	if (val)
+		ts->setup2 |= INV_X;
+	else
+		ts->setup2 &= ~INV_X;
+
+	if (ts->initialized)
+		err = i2c_smbus_write_byte_data(ts->client, COMMON_SETUP2,
+						ts->setup2);
+
+	return err;
+}
+
+static DEVICE_ATTR_RW(swap_xy);
+static DEVICE_ATTR_RW(inv_x);
+static DEVICE_ATTR_RW(inv_y);
+
+static struct attribute *rohm_ts_attrs[] = {
+	&dev_attr_swap_xy.attr,
+	&dev_attr_inv_x.attr,
+	&dev_attr_inv_y.attr,
+	NULL,
+};
+
+static const struct attribute_group rohm_ts_attr_group = {
+	.attrs = rohm_ts_attrs,
+};
+
+static int rohm_ts_device_init(struct rohm_ts_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	struct device *dev = &client->dev;
+	int ret;
+
+	/*
+	 * Wait 200usec for reset
+	 */
+	udelay(200);
+
+	/* Release analog reset */
+	ret = i2c_smbus_write_byte_data(client, SYSTEM, ANALOG_POWER_ON);
+	if (ret)
+		return ret;
+
+	/* Waiting for the analog warm-up, max. 200usec */
+	udelay(200);
+
+	/* clear all interrupts */
+	ret = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, EX_WDAT, 0);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, COMMON_SETUP1, 0);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, COMMON_SETUP2, ts->setup2);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, COMMON_SETUP3,
+					SEL_TBL_DEFAULT | EN_MULTI);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, THRESHOLD_GESTURE,
+					THRESHOLD_GESTURE_DEFAULT);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, INTERVAL_TIME,
+					INTERVAL_TIME_DEFAULT);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, CPU_FREQ, CPU_FREQ_10MHZ);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, PRM_SWOFF_TIME,
+					PRM_SWOFF_TIME_DEFAULT);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, ADC_CTRL, ADC_DIV_DEFAULT);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, ADC_WAIT, ADC_WAIT_DEFAULT);
+	if (ret)
+		return ret;
+
+	/*
+	 * Panel setup, these values change with the panel.
+	 */
+	ret = i2c_smbus_write_byte_data(client, STEP_X, STEP_X_DEFAULT);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, STEP_Y, STEP_Y_DEFAULT);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, OFFSET_X, OFFSET_X_DEFAULT);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, OFFSET_Y, OFFSET_Y_DEFAULT);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, THRESHOLD_TOUCH,
+					THRESHOLD_TOUCH_DEFAULT);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, EVR_XY, EVR_XY_DEFAULT);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, EVR_X, EVR_X_DEFAULT);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, EVR_Y, EVR_Y_DEFAULT);
+	if (ret)
+		return ret;
+
+	/* Fixed value settings */
+	ret = i2c_smbus_write_byte_data(client, CALIBRATION_ADJUST,
+					CALIBRATION_ADJUST_DEFAULT);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, SWCONT, SWCONT_DEFAULT);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, TEST1,
+					DUALTOUCH_STABILIZE_ON |
+					DUALTOUCH_REG_ON);
+	if (ret)
+		return ret;
+
+	ret = rohm_ts_load_firmware(client, BU21023_FIRMWARE_NAME);
+	if (ret) {
+		dev_err(dev, "Failed to firmware load\n");
+		return ret;
+	}
+
+	/*
+	 * Manual calibration results are not changed in same environment.
+	 * If the force calibration is performed,
+	 * the controller will not require calibration request interrupt
+	 * when the typical values are set to the calibration registers.
+	 */
+	ret = i2c_smbus_write_byte_data(client, CALIBRATION_REG1,
+					CALIBRATION_REG1_DEFAULT);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, CALIBRATION_REG2,
+					CALIBRATION_REG2_DEFAULT);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, CALIBRATION_REG3,
+					CALIBRATION_REG3_DEFAULT);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, FORCE_CALIBRATION,
+					FORCE_CALIBRATION_OFF);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, FORCE_CALIBRATION,
+					FORCE_CALIBRATION_ON);
+	if (ret)
+		return ret;
+
+	/* Clear all interrupts */
+	ret = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+	if (ret)
+		return ret;
+
+	ret = devm_request_threaded_irq(dev, client->irq, NULL,
+					rohm_ts_soft_irq, IRQF_TRIGGER_FALLING,
+					client->name, ts);
+	if (ret) {
+		dev_err(dev, "Unable to request IRQ\n");
+		return ret;
+	}
+
+	/* Enable coordinates update interrupt */
+	ret = i2c_smbus_write_byte_data(client, INT_MASK,
+					CALIBRATION_DONE |
+					SLEEP_OUT | SLEEP_IN |
+					PROGRAM_LOAD_DONE);
+	if (ret)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, ERR_MASK,
+					PROGRAM_LOAD_ERR | CPU_TIMEOUT |
+					ADC_TIMEOUT);
+	if (ret)
+		return ret;
+
+	/* controller CPU power on */
+	ret = i2c_smbus_write_byte_data(client, SYSTEM,
+					CPU_POWER_ON | ANALOG_POWER_ON);
+
+	return ret;
+}
+
+static int rohm_ts_open(struct input_dev *input_dev)
+{
+	struct rohm_ts_data *ts = input_get_drvdata(input_dev);
+	struct i2c_client *client = ts->client;
+	int ret;
+
+	if (!ts->initialized) {
+		ret = rohm_ts_device_init(ts);
+		if (ret) {
+			dev_err(&client->dev,
+				"Failed to device initialization\n");
+			return ret;
+		}
+
+		ts->initialized = true;
+	}
+
+	return 0;
+}
+
+static int rohm_bu21023_i2c_probe(struct i2c_client *client,
+				  const struct i2c_device_id *id)
+{
+	struct rohm_ts_data *ts;
+	struct i2c_adapter *adap = client->adapter;
+	struct device *dev = &client->dev;
+	struct input_dev *input_dev;
+	int ret;
+
+	if (!client->irq) {
+		dev_err(dev, "IRQ is not assigned\n");
+		return -EINVAL;
+	}
+
+	if (!adap->algo->master_xfer) {
+		dev_err(&adap->dev, "I2C level transfers not supported\n");
+		return -EOPNOTSUPP;
+	}
+
+	ts = devm_kzalloc(dev, sizeof(struct rohm_ts_data), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	ts->client = client;
+	ts->setup2 = MAF_1SAMPLE;
+	i2c_set_clientdata(client, ts);
+
+	input_dev = devm_input_allocate_device(dev);
+	if (!input_dev)
+		return -ENOMEM;
+
+	input_dev->name = BU21023_NAME;
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->open = rohm_ts_open;
+
+	ts->input_dev = input_dev;
+	input_set_drvdata(input_dev, ts);
+
+	__set_bit(EV_SYN, input_dev->evbit);
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(EV_ABS, input_dev->evbit);
+
+	__set_bit(BTN_TOUCH, input_dev->keybit);
+
+	input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+			     ROHM_TS_ABS_X_MIN, ROHM_TS_ABS_X_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+			     ROHM_TS_ABS_Y_MIN, ROHM_TS_ABS_Y_MAX, 0, 0);
+
+	input_set_abs_params(input_dev, ABS_X,
+			     ROHM_TS_ABS_X_MIN, ROHM_TS_ABS_X_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y,
+			     ROHM_TS_ABS_Y_MIN, ROHM_TS_ABS_Y_MAX, 0, 0);
+
+	ret = input_mt_init_slots(input_dev, BU21023_MAX_SLOTS,
+				  INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	if (ret) {
+		dev_err(dev, "Failed to multi touch slots initialization\n");
+		return ret;
+	}
+
+	ret = input_register_device(input_dev);
+	if (ret) {
+		dev_err(dev, "Unable to register input device\n");
+		return ret;
+	}
+
+	ret = sysfs_create_group(&dev->kobj, &rohm_ts_attr_group);
+	if (ret)
+		dev_err(dev, "Failed to create sysfs group\n");
+
+	return ret;
+}
+
+static int rohm_bu21023_i2c_remove(struct i2c_client *client)
+{
+	int ret;
+
+	sysfs_remove_group(&client->dev.kobj, &rohm_ts_attr_group);
+
+	ret = i2c_smbus_write_byte_data(client, SYSTEM,
+					ANALOG_POWER_ON | CPU_POWER_OFF);
+	if (ret)
+		return ret;
+
+	return i2c_smbus_write_byte_data(client, SYSTEM,
+					 ANALOG_POWER_OFF | CPU_POWER_OFF);
+}
+
+static const struct i2c_device_id rohm_bu21023_i2c_id[] = {
+	{BU21023_NAME, 0},
+	{},
+};
+
+MODULE_DEVICE_TABLE(i2c, rohm_bu21023_i2c_id);
+
+static struct i2c_driver rohm_bu21023_i2c_driver = {
+	.driver = {
+		.name = BU21023_NAME,
+	},
+	.probe = rohm_bu21023_i2c_probe,
+	.remove = rohm_bu21023_i2c_remove,
+	.id_table = rohm_bu21023_i2c_id,
+};
+
+module_i2c_driver(rohm_bu21023_i2c_driver);
+
+MODULE_DESCRIPTION("ROHM BU21023/24 Touchscreen driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("ROHM Co., Ltd.");
-- 
1.7.9.5


^ permalink raw reply related

* Re: [PATCH 2/3] drivers:input:ps2 Added sunxi A20 ps2 driver, changed makefile and Kconfig
From: Maxime Ripard @ 2014-12-05 10:01 UTC (permalink / raw)
  To: vishnupatekar
  Cc: linux-sunxi, robh+dt, benh, msalter, ralf, jdelvare, linux-kernel,
	linux-input, linux-arm-kernel, devicetree, dmitry.torokhov
In-Reply-To: <1417647224-27950-1-git-send-email-VishnuPatekar0510@gmail.com>

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

Hi,

Thanks for working on this.

First, make sure to read Documentation/SubmittingPatches. The
guidelines in there might seem tedious, but it's there to enforce that
we keep the kernel homogenous.

Most notably, you should make sure that:
  - you run scripts/checkpatch.pl on your patches
  - you have a commit log
  - your commit title stays below 80 chars
  - you have a Signed-off-by in your commit log

On Thu, Dec 04, 2014 at 04:23:44AM +0530, vishnupatekar wrote:
> ---
>  drivers/input/serio/Kconfig     |    9 ++
>  drivers/input/serio/Makefile    |    1 +
>  drivers/input/serio/sunxi-ps2.c |  305 +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 315 insertions(+)
>  create mode 100644 drivers/input/serio/sunxi-ps2.c
> 
> diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
> index bc2d474..1a86e41 100644
> --- a/drivers/input/serio/Kconfig
> +++ b/drivers/input/serio/Kconfig
> @@ -281,4 +281,13 @@ config HYPERV_KEYBOARD
>  	  To compile this driver as a module, choose M here: the module will
>  	  be called hyperv_keyboard.
>  
> +config SERIO_SUNXI_PS2
> +	tristate "Allwinner Sun7i-A20 PS/2 controller"
> +	default m
> +	help
> +	  Say Y here if you have Sun7i-A20 Allwinner PS/2 ports.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called sunxi-ps2.
> +
>  endif
> diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
> index 815d874..0fa0f78 100644
> --- a/drivers/input/serio/Makefile
> +++ b/drivers/input/serio/Makefile
> @@ -29,3 +29,4 @@ obj-$(CONFIG_SERIO_ARC_PS2)	+= arc_ps2.o
>  obj-$(CONFIG_SERIO_APBPS2)	+= apbps2.o
>  obj-$(CONFIG_SERIO_OLPC_APSP)	+= olpc_apsp.o
>  obj-$(CONFIG_HYPERV_KEYBOARD)	+= hyperv-keyboard.o
> +obj-$(CONFIG_SERIO_SUNXI_PS2)	+= sunxi-ps2.o
> diff --git a/drivers/input/serio/sunxi-ps2.c b/drivers/input/serio/sunxi-ps2.c
> new file mode 100644
> index 0000000..ccd7b29
> --- /dev/null
> +++ b/drivers/input/serio/sunxi-ps2.c
> @@ -0,0 +1,305 @@
> +/*
> + * sunxi-ps2.c Support for Allwinner A20 PS2 host controller
> + *
> + * Author: Aaron.maoye <leafy.myeh@newbietech.com>
> + *	   Vishnu Patekar <vishnupatekar0510@gmail.com>
> + * Based on sunxi-ps2.c 3.0 kernel

Mentionning that it's Allwinner's 3.0 kernel would be good, it doesn't
make much sense otherwise.

> +#include <linux/module.h>
> +#include <linux/serio.h>
> +#include <linux/interrupt.h>
> +#include <linux/errno.h>
> +#include <linux/slab.h>
> +#include <linux/list.h>
> +#include <linux/io.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +
> +#define DRIVER_NAME		"sunxi-ps2"
> +
> +#define RESSIZE(res)        (((res)->end - (res)->start)+1)
> +
> +#define SW_PS2_GCTRL        (0x00)
> +#define SW_PS2_DATA         (0x04)
> +#define SW_PS2_LCTRL        (0x08)
> +#define SW_PS2_LSTAT        (0x0c)
> +#define SW_PS2_FCTRL        (0x10)
> +#define SW_PS2_FSTAT        (0x14)
> +#define SW_PS2_CLKDR        (0x18)

The SW_ prefix is a left over from Allwinner I suppose?

Using a prefix like sun4i_ps2/sun7i_ps2 depending on the outcome of
the discussion you had with Hans would seem better, across all your
driver.

> +
> +/* SW_PS2_GCTRL */
> +#define SWPS2_BUSEN         (1 << 0)
> +#define SWPS2_MASTER        (1 << 1)
> +#define SWPS2_RESET         (1 << 2)
> +#define SWPS2_INTEN         (1 << 3)

We usually have the convention of having the register name mentionned
in the bits offsets, and the bits declared right after their register.

Something like:

#define SUN4I_PS2_GCTRL_REG	0x00
#define SUN4I_PS2_GCTRL_BUS_EN		BIT(0)

#define SUN4I_PS2_LCTRL_REG	0x08
#define SUN4I_PS2_LCTRL_PAR_ERR_EN	BIT(1)

etc....

> +#define SWPS2_INTFLAG       (1 << 3)

This is defined to the same value than _INTEN, is this intentionnal?

> +
> +/* SW_PS2_LCTRL */
> +#define SWPS2_LCTL_NOACK        (0x0 << 18)
> +#define SWPS2_LCTL_TXDTOEN      (0x1 << 8)
> +#define SWPS2_LCTL_STOPERREN    (0x1 << 3)
> +#define SWPS2_LCTL_ACKERREN     (0x1 << 2)
> +#define SWPS2_LCTL_PARERREN     (0x1 << 1)
> +#define SWPS2_LCTL_RXDTOEN      (0x1 << 0)
> +
> +/* SW_PS2_FSTAT */
> +#define SWPS2_FSTA_RXRDY        (1 << 0)
> +#define SWPS2_FSTA_RXOF         (1 << 1)
> +#define SWPS2_FSTA_RXUF         (1 << 2)
> +#define SWPS2_FSTA_TXRDY        (1 << 8)
> +#define SWPS2_FSTA_TXOF         (1 << 9)
> +#define SWPS2_FSTA_TXUF         (1 << 10)
> +
> +#define SW_PS2_SAMPLE_CLK      (1000000)
> +#define SW_PS2_SCLK            (125000)
> +
> +struct sunxips2data {
> +	int irq;
> +	spinlock_t ps2_lock;
> +	void __iomem *base_address;	/* virt address of control registers*/
> +	struct serio *serio;		/* serio*/
> +	struct device *dev;
> +	struct	clk	*pclk;
> +};
> +
> +/*********************/
> +/* Interrupt handler */
> +/*********************/
> +static irqreturn_t sunxips2_interrupt(int irq, void *dev_id)
> +{
> +	struct sunxips2data *drvdata = dev_id;
> +	u32 intr_status;
> +	u32 fifo_status;
> +	unsigned char byte;
> +	u32 rval;
> +	u32 error = 0;
> +
> +	spin_lock(&drvdata->ps2_lock);
> +
> +	/* Get the PS/2 interrupts and clear them */
> +	intr_status  = readl(drvdata->base_address + SW_PS2_LSTAT);
> +	fifo_status  = readl(drvdata->base_address + SW_PS2_FSTAT);
> +
> +	/*Check Line Status Register*/
> +	if (intr_status & 0x10f) {
> +		if (intr_status & 0x08)
> +			dev_info(drvdata->dev, "PS/2 Stop Bit Error!");
> +		if (intr_status & 0x04)
> +			dev_info(drvdata->dev, "PS/2 Acknowledge Error!\n");
> +		if (intr_status & 0x02)
> +			dev_info(drvdata->dev, "PS/2 Parity Error!\n");
> +		if (intr_status & 0x100)
> +			dev_info(drvdata->dev, "PS/2 Transmit Data Timeout!\n");
> +		if (intr_status & 0x01)
> +			dev_info(drvdata->dev, "PS/2 Receive Data Timeout!\n");
> +
> +		writel(readl(drvdata->base_address + SW_PS2_GCTRL)|0x4, drvdata->base_address + SW_PS2_GCTRL);/*reset PS/2 controller*/
> +		writel(0x10f, drvdata->base_address + SW_PS2_LSTAT);
> +
> +		error = 1;
> +	}
> +
> +    /*Check FIFO Status Register*/
> +	if (fifo_status & 0x0606) {
> +		if (fifo_status & 0x400)
> +			dev_info(drvdata->dev, "PS/2 Tx FIFO Underflow!\n");
> +		if (fifo_status & 0x200)
> +			dev_info(drvdata->dev, "PS/2 Tx FIFO Overflow!\n");
> +		if (fifo_status & 0x04)
> +			dev_info(drvdata->dev, "PS/2 Rx FIFO Underflow!\n");
> +		if (fifo_status & 0x02)
> +			dev_info(drvdata->dev, "PS/2 Rx FIFO Overflow!\n");
> +
> +		writel(readl(drvdata->base_address + SW_PS2_GCTRL)|0x4, drvdata->base_address + SW_PS2_GCTRL); /*reset PS/2 controller*/
> +		writel(0x707, drvdata->base_address + SW_PS2_FSTAT);
> +		error = 1;
> +	}
> +
> +	rval = (fifo_status >> 16) & 0x3;
> +	while (!error && rval--) {
> +		byte = readl(drvdata->base_address + SW_PS2_DATA) & 0xff;
> +		dev_info(drvdata->dev, "PS/2 Receive %02x\n", byte);
> +		serio_interrupt(drvdata->serio, byte, 0);
> +	}
> +
> +	writel(intr_status, drvdata->base_address + SW_PS2_LSTAT);
> +	writel(fifo_status, drvdata->base_address + SW_PS2_FSTAT);
> +
> +	spin_unlock(&drvdata->ps2_lock);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int sunxips2_open(struct serio *pserio)
> +{
> +	struct sunxips2data *drvdata = pserio->port_data;
> +	u32 src_clk = 0;
> +	u32 clk_scdf;
> +	u32 clk_pcdf;
> +	u32 rval;
> +
> +	/*Set Line Control And Enable Interrupt*/
> +	rval = SWPS2_LCTL_TXDTOEN|SWPS2_LCTL_STOPERREN|SWPS2_LCTL_ACKERREN|SWPS2_LCTL_PARERREN|SWPS2_LCTL_RXDTOEN;
> +	writel(rval, drvdata->base_address + SW_PS2_LCTRL);
> +
> +	/*Reset FIFO*/
> +	writel(0x3<<16 | 0x607, drvdata->base_address + SW_PS2_FCTRL);
> +
> +	src_clk = clk_get_rate(drvdata->pclk);
> +
> +	if (!src_clk) {
> +		dev_info(drvdata->dev, "w_ps2c_set_sclk error, source clock is 0.");
> +		return -1;
> +	}
> +
> +	/*Set Clock Divider Register*/
> +	clk_scdf = ((src_clk + (SW_PS2_SAMPLE_CLK>>1)) / SW_PS2_SAMPLE_CLK - 1);
> +	clk_pcdf = ((SW_PS2_SAMPLE_CLK + (SW_PS2_SCLK>>1)) / SW_PS2_SCLK - 1);
> +	rval = (clk_scdf<<8) | clk_pcdf;/* | (PS2_DEBUG_SEL<<16);*/

What is this supposed to do? Calculating and rounding the dividers?

You should look into DIV_ROUND_* if that's so

> +	writel(rval, drvdata->base_address + SW_PS2_CLKDR);
> +
> +	/*Set Global Control Register*/
> +	rval = SWPS2_RESET|SWPS2_INTEN|SWPS2_MASTER|SWPS2_BUSEN;
> +	writel(rval, drvdata->base_address + SW_PS2_GCTRL);
> +
> +	udelay(100);

Why is that udelay needed?

> +
> +	return 0;
> +}
> +
> +static void sunxips2_close(struct serio *pserio)
> +{
> +	struct sunxips2data *drvdata = pserio->port_data;
> +
> +	spin_lock(&drvdata->ps2_lock);

spin_lock_irqsave would be better I guess.

> +	/* Disable the PS2 interrupts */
> +	writel(0, drvdata->base_address + SW_PS2_GCTRL);
> +	spin_unlock(&drvdata->ps2_lock);
> +}
> +
> +static int sunxips2_write(struct serio *pserio, unsigned char val)
> +{
> +	struct sunxips2data *drvdata = (struct sunxips2data *)pserio->port_data;
> +	u32 timeout = 10000;
> +
> +	do {
> +		if (readl(drvdata->base_address + SW_PS2_FSTAT) & SWPS2_FSTA_TXRDY) {
> +			writel(val, drvdata->base_address + SW_PS2_DATA);
> +			return 0;
> +		}
> +	} while (timeout--);

Please use time_before() here

> +	return -1;

And return a meaningful error.

> +static int sunxips2_probe(struct platform_device *ofdev)

The ofdev name is measleading. This is a platform_device structure, it
could be probed by other mechanisms than OF, so please use a different
name here. Usually pdev is used.

> +{
> +	struct resource *res; /* IO mem resources */
> +	struct sunxips2data *drvdata;
> +	struct serio *serio;
> +	struct device *dev = &ofdev->dev;
> +	unsigned int irq;
> +	int error;
> +
> +	drvdata = devm_kzalloc(dev, sizeof(struct sunxips2data), GFP_KERNEL);
> +	serio = devm_kzalloc(dev, sizeof(struct serio), GFP_KERNEL);
> +	if (!drvdata || !serio)
> +		error = -ENOMEM;
> +
> +	/* Request clock */
> +	drvdata->pclk = clk_get(dev, NULL);

You can use devm_clk_get.

> +	if (IS_ERR(drvdata->pclk))
> +		dev_dbg(dev, "couldn't get clock %li\n",
> +			PTR_ERR(drvdata->pclk));

This is an error, you should treat it as such.

> +	if (!IS_ERR(drvdata->pclk)) {
> +		error = clk_prepare_enable(drvdata->pclk);
> +		if (error < 0) {
> +			dev_err(dev, "failed to enable clock %d\n", error);
> +			return error;
> +		}
> +	}
> +
> +	/* IO */
> +	res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
> +	drvdata->base_address = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(drvdata->base_address)) {
> +		dev_err(dev, "failed to map registers\n");
> +		error = PTR_ERR(drvdata->base_address);
> +	}

ditto.

> +	serio->id.type = SERIO_8042;
> +	serio->write = sunxips2_write;
> +	serio->open = sunxips2_open;
> +	serio->close = sunxips2_close;
> +	serio->port_data = drvdata;
> +	serio->dev.parent = dev;
> +	strlcpy(serio->name, dev_name(dev), sizeof(serio->name));
> +	strlcpy(serio->phys, dev_name(dev), sizeof(serio->phys));
> +
> +	platform_set_drvdata(ofdev, drvdata);
> +	serio_register_port(serio);
> +
> +	/* Get IRQ for the device */
> +	irq = irq_of_parse_and_map(ofdev->dev.of_node, 0);
> +	if (!irq) {
> +		dev_err(dev, "no IRQ found\n");
> +		return -ENODEV;
> +	}

You can use platform_get_irq, for consistency with how you retrieve
resources.

You're also no unregistering the driver from the serio framework.

> +	drvdata->irq = irq;
> +	drvdata->serio = serio;
> +	drvdata->dev = dev;
> +	error = devm_request_any_context_irq(drvdata->dev, drvdata->irq, &sunxips2_interrupt, 0,
> +				DRIVER_NAME, drvdata);

request_irq is enough here.

> +	if (error) {
> +		dev_err(drvdata->dev,
> +			"Couldn't allocate interrupt %d : error: %d\n", drvdata->irq, error);
> +		return error;
> +	}
> +	return 0;		/* success */
> +}
> +
> +static int sunxips2_remove(struct platform_device *of_dev)
> +{
> +	struct sunxips2data *drvdata = platform_get_drvdata(of_dev);
> +
> +	if (!IS_ERR(drvdata->pclk)) {
> +		clk_disable_unprepare(drvdata->pclk);
> +		clk_put(drvdata->pclk);
> +	}
> +	serio_unregister_port(drvdata->serio);
> +	mdelay(2);

Why a mdelay?

> +	return 0;
> +}
> +
> +/* Match table for of_platform binding */
> +static const struct of_device_id sunxips2_of_match[] = {
> +	{ .compatible = "allwinner,sun7i-a20-ps2", },
> +	{ },
> +};
> +
> +MODULE_DEVICE_TABLE(of, sunxips2_of_match);
> +
> +/*platform driver structure*/
> +static struct platform_driver sunxips2_of_driver = {
> +	.probe		= sunxips2_probe,
> +	.remove		= sunxips2_remove,
> +	.driver = {
> +		.name = DRIVER_NAME,
> +		.owner = THIS_MODULE,

You can drop the owner field, it's already set by
module_platform_driver.

> +		.of_match_table = sunxips2_of_match,
> +	},
> +};
> +module_platform_driver(sunxips2_of_driver);
> +
> +MODULE_AUTHOR("Aaron.maoye<leafy.myeh@newbietech.com, "
> +		"Vishnu Patekar <vishnupatekar0510@gmail.com>");

Usually, you define two MODULE_AUTHOR.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

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

^ permalink raw reply

* Re: [PATCH 2/3] drivers:input:ps2 Added sunxi A20 ps2 driver, changed makefile and Kconfig
From: Arnd Bergmann @ 2014-12-05 10:33 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: vishnupatekar, maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, ralf-6z/3iImG2C8G8FEW9MqTrA,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, msalter-H+wXaHxf7aLQT0dZR+AlfA,
	vishnupatekar, jdelvare-l3A5Bk7waGM
In-Reply-To: <1417647224-27950-1-git-send-email-VishnuPatekar0510-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

On Thursday 04 December 2014 04:23:44 vishnupatekar wrote:
> +
> +#define DRIVER_NAME		"sunxi-ps2"
> +
> +#define RESSIZE(res)        (((res)->end - (res)->start)+1)

Remove this and use the existing resource_size() function

> +
> +struct sunxips2data {
> +	int irq;
> +	spinlock_t ps2_lock;
> +	void __iomem *base_address;	/* virt address of control registers*/
> +	struct serio *serio;		/* serio*/
> +	struct device *dev;
> +	struct	clk	*pclk;
> +};

As this is dynamically allocated, better embed the serio member
directly to avoid allocating both separately.

> +static int sunxips2_open(struct serio *pserio)
> +{
> +	struct sunxips2data *drvdata = pserio->port_data;
> +	u32 src_clk = 0;
> +	u32 clk_scdf;
> +	u32 clk_pcdf;
> +	u32 rval;
> +
> +	/*Set Line Control And Enable Interrupt*/
> +	rval = SWPS2_LCTL_TXDTOEN|SWPS2_LCTL_STOPERREN|SWPS2_LCTL_ACKERREN|SWPS2_LCTL_PARERREN|SWPS2_LCTL_RXDTOEN;
> +	writel(rval, drvdata->base_address + SW_PS2_LCTRL);
> +
> +	/*Reset FIFO*/
> +	writel(0x3<<16 | 0x607, drvdata->base_address + SW_PS2_FCTRL);
> +
> +	src_clk = clk_get_rate(drvdata->pclk);
> +
> +	if (!src_clk) {
> +		dev_info(drvdata->dev, "w_ps2c_set_sclk error, source clock is 0.");
> +		return -1;
> +	}
> +
> +	/*Set Clock Divider Register*/
> +	clk_scdf = ((src_clk + (SW_PS2_SAMPLE_CLK>>1)) / SW_PS2_SAMPLE_CLK - 1);
> +	clk_pcdf = ((SW_PS2_SAMPLE_CLK + (SW_PS2_SCLK>>1)) / SW_PS2_SCLK - 1);
> +	rval = (clk_scdf<<8) | clk_pcdf;/* | (PS2_DEBUG_SEL<<16);*/
> +	writel(rval, drvdata->base_address + SW_PS2_CLKDR);
> +
> +	/*Set Global Control Register*/
> +	rval = SWPS2_RESET|SWPS2_INTEN|SWPS2_MASTER|SWPS2_BUSEN;
> +	writel(rval, drvdata->base_address + SW_PS2_GCTRL);
> +
> +	udelay(100);

100 microseconds is a rather long time to block the CPU for, so this
needs a comment explaining why the particular delay is needed and why
you can't use usleep_range() instead.

> +static void sunxips2_close(struct serio *pserio)
> +{
> +       struct sunxips2data *drvdata = pserio->port_data;
> +
> +       spin_lock(&drvdata->ps2_lock);
> +       /* Disable the PS2 interrupts */
> +       writel(0, drvdata->base_address + SW_PS2_GCTRL);
> +       spin_unlock(&drvdata->ps2_lock);
> +}

The locking is wrong here: you take the lock without disabling the
interrupts first, so if the interrupt happens between the spin_lock()
call and the writel(), the kernel will deadlock.

You will either have to use spin_lock_irq() here, or find a justification
for dropping the lock entirely.

> +static int sunxips2_write(struct serio *pserio, unsigned char val)
> +{
> +	struct sunxips2data *drvdata = (struct sunxips2data *)pserio->port_data;
> +	u32 timeout = 10000;
> +
> +	do {
> +		if (readl(drvdata->base_address + SW_PS2_FSTAT) & SWPS2_FSTA_TXRDY) {
> +			writel(val, drvdata->base_address + SW_PS2_DATA);
> +			return 0;
> +		}
> +	} while (timeout--);
> +
> +	return -1;
> +}

We never return '-1' from in-kernel functions. Either make this a bool
argument, or return a proper errno.h value. This should probably return
-EIO.

> +	drvdata->irq = irq;
> +	drvdata->serio = serio;
> +	drvdata->dev = dev;
> +	error = devm_request_any_context_irq(drvdata->dev, drvdata->irq, &sunxips2_interrupt, 0,
> +				DRIVER_NAME, drvdata);
> +	if (error) {
> +		dev_err(drvdata->dev,
> +			"Couldn't allocate interrupt %d : error: %d\n", drvdata->irq, error);
> +		return error;
> +	}
> +	return 0;		/* success */
> +}

Why any_context?

	Arnd

^ permalink raw reply

* Re: [PATCH 2/3] drivers:input:ps2 Added sunxi A20 ps2 driver, changed makefile and Kconfig
From: Dmitry Torokhov @ 2014-12-05 15:01 UTC (permalink / raw)
  To: Arnd Bergmann, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: vishnupatekar, maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, ralf-6z/3iImG2C8G8FEW9MqTrA,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, msalter-H+wXaHxf7aLQT0dZR+AlfA,
	vishnupatekar, jdelvare-l3A5Bk7waGM
In-Reply-To: <12237550.afophX20rO@wuerfel>

Hi Arnd,

On December 5, 2014 2:33:11 AM PST, Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org> wrote:
>On Thursday 04 December 2014 04:23:44 vishnupatekar wrote:
>> +
>> +struct sunxips2data {
>> +	int irq;
>> +	spinlock_t ps2_lock;
>> +	void __iomem *base_address;	/* virt address of control registers*/
>> +	struct serio *serio;		/* serio*/
>> +	struct device *dev;
>> +	struct	clk	*pclk;
>> +};
>
>As this is dynamically allocated, better embed the serio member
>directly to avoid allocating both separately.

That would be wrong - serio is refcounted and it may outlive instance of sunxips2data you embedded it into.


Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
From: Dmitry Torokhov @ 2014-12-05 15:04 UTC (permalink / raw)
  To: jeffrey.lin, shc_work, bleung, lee.jones, charliemooney
  Cc: KP.li, linux-kernel, linux-input, jeffrey.lin
In-Reply-To: <1417767301-20141-1-git-send-email-jeffrey.lin@rad-ic.com>

Hi Jeffrey,

On December 5, 2014 12:15:01 AM PST, "jeffrey.lin" <yajohn@gmail.com> wrote:
>From: "jeffrey.lin" <jeffrey.lin@rad-ic.com>
>
>This patch is porting Raydium I2C touch driver. Developer can enable
>raydium touch driver by modifying define
>"CONFIG_TOUCHSCREEN_RM_TS".
>
>Signed-off-by: jeffrey.lin@rad-ic.com
>---
> drivers/input/touchscreen/Kconfig  | 12 ++++++++++++
> drivers/input/touchscreen/Makefile |  1 +
> 2 files changed, 13 insertions(+)
>
>diff --git a/drivers/input/touchscreen/Kconfig
>b/drivers/input/touchscreen/Kconfig
>index 3ce9181..d0324d2 100644
>--- a/drivers/input/touchscreen/Kconfig
>+++ b/drivers/input/touchscreen/Kconfig
>@@ -955,4 +955,16 @@ config TOUCHSCREEN_ZFORCE
> 	  To compile this driver as a module, choose M here: the
> 	  module will be called zforce_ts.
> 
>+config TOUCHSCREEN_RM_TS
>+	tristate "Raydium I2C Touchscreen"
>+	depends on I2C
>+	help
>+	  Say Y here if you have Raydium series I2C touchscreen,
>+	  such as RM31100 , connected to your system.
>+
>+	  If unsure, say N.
>+
>+	  To compile this driver as a module, choose M here: the
>+	  module will be called rm31100_ts.
>+
> endif
>diff --git a/drivers/input/touchscreen/Makefile
>b/drivers/input/touchscreen/Makefile
>index 687d5a7..3220f66 100644
>--- a/drivers/input/touchscreen/Makefile
>+++ b/drivers/input/touchscreen/Makefile
>@@ -78,3 +78,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+=
>zylonite-wm97xx.o
> obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o
> obj-$(CONFIG_TOUCHSCREEN_TPS6507X)	+= tps6507x-ts.o
> obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
>+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= rm31100_ts.o

The source to the driver itself is missing, you only sent make file and kconfig changes.


Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH 2/3] drivers:input:ps2 Added sunxi A20 ps2 driver, changed makefile and Kconfig
From: Arnd Bergmann @ 2014-12-05 15:50 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, vishnupatekar,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, ralf-6z/3iImG2C8G8FEW9MqTrA,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, msalter-H+wXaHxf7aLQT0dZR+AlfA,
	vishnupatekar, jdelvare-l3A5Bk7waGM
In-Reply-To: <63D786FF-B293-4FC1-AD71-D99515DE8E3D-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

On Friday 05 December 2014 07:01:17 Dmitry Torokhov wrote:
> 
> On December 5, 2014 2:33:11 AM PST, Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org> wrote:
> >On Thursday 04 December 2014 04:23:44 vishnupatekar wrote:
> >> +
> >> +struct sunxips2data {
> >> +    int irq;
> >> +    spinlock_t ps2_lock;
> >> +    void __iomem *base_address;     /* virt address of control registers*/
> >> +    struct serio *serio;            /* serio*/
> >> +    struct device *dev;
> >> +    struct  clk     *pclk;
> >> +};
> >
> >As this is dynamically allocated, better embed the serio member
> >directly to avoid allocating both separately.
> 
> That would be wrong - serio is refcounted and it may outlive instance of sunxips2data you embedded it into.

Ok, I see. I guess in this case the use of devm_kzalloc for serio is a bug,
because that would lead to a double free upon module unload, right?

	Arnd

^ permalink raw reply

* Re: [PATCH 2/3] drivers:input:ps2 Added sunxi A20 ps2 driver, changed makefile and Kconfig
From: Dmitry Torokhov @ 2014-12-05 15:53 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, vishnupatekar,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, ralf-6z/3iImG2C8G8FEW9MqTrA,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, msalter-H+wXaHxf7aLQT0dZR+AlfA,
	vishnupatekar, jdelvare-l3A5Bk7waGM
In-Reply-To: <2906898.Fr1ixVkiAI@wuerfel>

On December 5, 2014 7:50:18 AM PST, Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org> wrote:
>On Friday 05 December 2014 07:01:17 Dmitry Torokhov wrote:
>> 
>> On December 5, 2014 2:33:11 AM PST, Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
>wrote:
>> >On Thursday 04 December 2014 04:23:44 vishnupatekar wrote:
>> >> +
>> >> +struct sunxips2data {
>> >> +    int irq;
>> >> +    spinlock_t ps2_lock;
>> >> +    void __iomem *base_address;     /* virt address of control
>registers*/
>> >> +    struct serio *serio;            /* serio*/
>> >> +    struct device *dev;
>> >> +    struct  clk     *pclk;
>> >> +};
>> >
>> >As this is dynamically allocated, better embed the serio member
>> >directly to avoid allocating both separately.
>> 
>> That would be wrong - serio is refcounted and it may outlive instance
>of sunxips2data you embedded it into.
>
>Ok, I see. I guess in this case the use of devm_kzalloc for serio is a
>bug,
>because that would lead to a double free upon module unload, right?
>

Exactly. I already mentioned that in my review.

Thanks.

-- 
Dmitry

^ permalink raw reply

* [PATCH v2 2/3] HID: wacom: Initialize MT slots for generic devices at post_parse_hid
From: Jason Gerecke @ 2014-12-05 21:37 UTC (permalink / raw)
  To: jkosina; +Cc: linux-input, benjamin.tissoires, pinglinux, Jason Gerecke

If a HID descriptor places HID_DG_CONTACTID before HID_DG_X and HID_DG_Y then
the ABS_X and ABS_Y will not be automatically initialized by the call to
input_mt_init_slots. To ensure that this is not a problem, we relocate that
call to occur after HID parsing has been completed and we've initalized all
the multitouch axes.

Signed-off-by: Jason Gerecke <killertofu@gmail.com>
---
Changes from v1:
 * Patch renamed from "HID: wacom: Manually declare ABS_{X,Y} for pointer emulation"
 * Patch moves call to input_mt_init_slots rather than manually declare ABS_{X,Y}

 drivers/hid/wacom_sys.c | 18 ++++++++++++++++++
 drivers/hid/wacom_wac.c |  3 ---
 2 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index eb55316..872aa0b 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -13,6 +13,7 @@
 
 #include "wacom_wac.h"
 #include "wacom.h"
+#include <linux/input/mt.h>
 
 #define WAC_MSG_RETRIES		5
 
@@ -236,6 +237,21 @@ static void wacom_usage_mapping(struct hid_device *hdev,
 		wacom_wac_usage_mapping(hdev, field, usage);
 }
 
+static void wacom_post_parse_hid(struct hid_device *hdev,
+				 struct wacom_features *features)
+{
+	struct wacom *wacom = hid_get_drvdata(hdev);
+	struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+
+	if (features->type == HID_GENERIC) {
+		/* Any last-minute generic device setup */
+		if (features->touch_max > 1) {
+			input_mt_init_slots(wacom_wac->input, wacom_wac->features.touch_max,
+				    INPUT_MT_DIRECT);
+		}
+	}
+}
+
 static void wacom_parse_hid(struct hid_device *hdev,
 			   struct wacom_features *features)
 {
@@ -270,6 +286,8 @@ static void wacom_parse_hid(struct hid_device *hdev,
 				wacom_usage_mapping(hdev, hreport->field[i],
 						hreport->field[i]->usage + j);
 	}
+
+	wacom_post_parse_hid(hdev, features);
 }
 
 static int wacom_hid_set_device_mode(struct hid_device *hdev)
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index a8a19a5..c33f889 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -1381,7 +1381,6 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
 {
 	struct wacom *wacom = hid_get_drvdata(hdev);
 	struct wacom_wac *wacom_wac = &wacom->wacom_wac;
-	struct input_dev *input = wacom_wac->input;
 	unsigned touch_max = wacom_wac->features.touch_max;
 
 	switch (usage->hid) {
@@ -1400,8 +1399,6 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
 					ABS_MT_POSITION_Y, 4);
 		break;
 	case HID_DG_CONTACTID:
-		input_mt_init_slots(input, wacom_wac->features.touch_max,
-			INPUT_MT_DIRECT);
 		break;
 	case HID_DG_INRANGE:
 		break;
-- 
2.1.3


^ permalink raw reply related

* [PATCH v2 3/3] HID: wacom: Report input events for each finger on generic devices
From: Jason Gerecke @ 2014-12-05 21:37 UTC (permalink / raw)
  To: jkosina; +Cc: linux-input, benjamin.tissoires, pinglinux, Jason Gerecke
In-Reply-To: <1417815453-10618-1-git-send-email-killertofu@gmail.com>

The existing generic touch code only reports events after reading an
entire HID report, which practically means that only data about the last
contact in a report will ever be provided to userspace. This patch uses
a trick from hid-multitouch.c to discover what type of field is at the
end of each contact; when such a field is encountered all the stored
contact data will be reported.

Signed-off-by: Jason Gerecke <killertofu@gmail.com>
---
Changes from v1:
 * Patch renamed from "HID: wacom: Report input events immediately upon receipt"
 * Instead of reporting on-reciept, events are bundled and sent at the end of each contact
 * Adds code to discover 'last_slot_field' based on hid-multitouch.c implementation

 drivers/hid/wacom_wac.c | 94 ++++++++++++++++++++++++++++++++-----------------
 drivers/hid/wacom_wac.h |  1 +
 2 files changed, 63 insertions(+), 32 deletions(-)

diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index c33f889..75fc16e 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -1381,35 +1381,68 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
 {
 	struct wacom *wacom = hid_get_drvdata(hdev);
 	struct wacom_wac *wacom_wac = &wacom->wacom_wac;
-	unsigned touch_max = wacom_wac->features.touch_max;
+	struct wacom_features *features = &wacom_wac->features;
 
 	switch (usage->hid) {
 	case HID_GD_X:
-		if (touch_max == 1)
+		features->last_slot_field = usage->hid;
+		if (features->touch_max == 1)
 			wacom_map_usage(wacom, usage, field, EV_ABS, ABS_X, 4);
 		else
 			wacom_map_usage(wacom, usage, field, EV_ABS,
 					ABS_MT_POSITION_X, 4);
 		break;
 	case HID_GD_Y:
-		if (touch_max == 1)
+		features->last_slot_field = usage->hid;
+		if (features->touch_max == 1)
 			wacom_map_usage(wacom, usage, field, EV_ABS, ABS_Y, 4);
 		else
 			wacom_map_usage(wacom, usage, field, EV_ABS,
 					ABS_MT_POSITION_Y, 4);
 		break;
 	case HID_DG_CONTACTID:
+		features->last_slot_field = usage->hid;
 		break;
 	case HID_DG_INRANGE:
+		features->last_slot_field = usage->hid;
 		break;
 	case HID_DG_INVERT:
+		features->last_slot_field = usage->hid;
 		break;
 	case HID_DG_TIPSWITCH:
+		features->last_slot_field = usage->hid;
 		wacom_map_usage(wacom, usage, field, EV_KEY, BTN_TOUCH, 0);
 		break;
 	}
 }
 
+static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac,
+		struct input_dev *input)
+{
+	struct hid_data *hid_data = &wacom_wac->hid_data;
+	bool mt = wacom_wac->features.touch_max > 1;
+	bool prox = hid_data->tipswitch &&
+		    !wacom_wac->shared->stylus_in_proximity;
+
+	if (mt) {
+		int slot;
+
+		slot = input_mt_get_slot_by_key(input, hid_data->id);
+		input_mt_slot(input, slot);
+		input_mt_report_slot_state(input, MT_TOOL_FINGER, prox);
+	}
+	else {
+		input_report_key(input, BTN_TOUCH, prox);
+	}
+
+	if (prox) {
+		input_report_abs(input, mt ? ABS_MT_POSITION_X : ABS_X,
+				 hid_data->x);
+		input_report_abs(input, mt ? ABS_MT_POSITION_Y : ABS_Y,
+				 hid_data->y);
+	}
+}
+
 static int wacom_wac_finger_event(struct hid_device *hdev,
 		struct hid_field *field, struct hid_usage *usage, __s32 value)
 {
@@ -1432,36 +1465,35 @@ static int wacom_wac_finger_event(struct hid_device *hdev,
 	}
 
 
+	if (usage->usage_index + 1 == field->report_count) {
+		if (usage->hid == wacom_wac->features.last_slot_field)
+			wacom_wac_finger_slot(wacom_wac, wacom_wac->input);
+	}
+
 	return 0;
 }
 
-static void wacom_wac_finger_mt_report(struct wacom_wac *wacom_wac,
-		struct input_dev *input, bool touch)
+static int wacom_wac_finger_touches(struct hid_device *hdev)
 {
-	int slot;
-	struct hid_data *hid_data = &wacom_wac->hid_data;
+	struct wacom *wacom = hid_get_drvdata(hdev);
+	struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+	struct input_dev *input = wacom_wac->input;
+	unsigned touch_max = wacom_wac->features.touch_max;
+	int count = 0;
+	int i;
 
-	slot = input_mt_get_slot_by_key(input, hid_data->id);
+	if (touch_max == 1)
+		return wacom_wac->hid_data.tipswitch &&
+		       !wacom_wac->shared->stylus_in_proximity;
 
-	input_mt_slot(input, slot);
-	input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
-	if (touch) {
-		input_report_abs(input, ABS_MT_POSITION_X, hid_data->x);
-		input_report_abs(input, ABS_MT_POSITION_Y, hid_data->y);
+	for (i = 0; i < input->mt->num_slots; i++) {
+		struct input_mt_slot *ps = &input->mt->slots[i];
+		int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
+		if (id >= 0)
+			count++;
 	}
-	input_mt_sync_frame(input);
-}
-
-static void wacom_wac_finger_single_touch_report(struct wacom_wac *wacom_wac,
-		struct input_dev *input, bool touch)
-{
-	struct hid_data *hid_data = &wacom_wac->hid_data;
 
-	if (touch) {
-		input_report_abs(input, ABS_X, hid_data->x);
-		input_report_abs(input, ABS_Y, hid_data->y);
-	}
-	input_report_key(input, BTN_TOUCH, touch);
+	return count;
 }
 
 static void wacom_wac_finger_report(struct hid_device *hdev,
@@ -1470,18 +1502,16 @@ static void wacom_wac_finger_report(struct hid_device *hdev,
 	struct wacom *wacom = hid_get_drvdata(hdev);
 	struct wacom_wac *wacom_wac = &wacom->wacom_wac;
 	struct input_dev *input = wacom_wac->input;
-	bool touch = wacom_wac->hid_data.tipswitch &&
-		     !wacom_wac->shared->stylus_in_proximity;
-	unsigned touch_max = wacom_wac->features.touch_max;
 
-	if (touch_max > 1)
-		wacom_wac_finger_mt_report(wacom_wac, input, touch);
+	if (wacom_wac->features.touch_max > 1)
+		input_mt_sync_frame(input);
 	else
-		wacom_wac_finger_single_touch_report(wacom_wac, input, touch);
+		wacom_wac_finger_slot(wacom_wac, input);
+
 	input_sync(input);
 
 	/* keep touch state for pen event */
-	wacom_wac->shared->touch_down = touch;
+	wacom_wac->shared->touch_down = wacom_wac_finger_touches(hdev);
 }
 
 #define WACOM_PEN_FIELD(f)	(((f)->logical == HID_DG_STYLUS) || \
diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h
index 128cbb3..6e256206 100644
--- a/drivers/hid/wacom_wac.h
+++ b/drivers/hid/wacom_wac.h
@@ -144,6 +144,7 @@ struct wacom_features {
 	int pktlen;
 	bool check_for_hid_type;
 	int hid_type;
+	int last_slot_field;
 };
 
 struct wacom_shared {
-- 
2.1.3


^ permalink raw reply related

* Re: [PATCH 1/4] alps: v7: Document the v7 touchpad packet protocol
From: Benjamin Tissoires @ 2014-12-05 21:58 UTC (permalink / raw)
  To: Hans de Goede; +Cc: Dmitry Torokhov, linux-input, Benjamin Tissoires
In-Reply-To: <1417613268-13674-1-git-send-email-hdegoede@redhat.com>

On Wed, Dec 3, 2014 at 8:27 AM, Hans de Goede <hdegoede@redhat.com> wrote:
> Add a table documenting where all the bits are in the v7 touchpad packets.
>
> Cc: stable@vger.kernel.org # 3.17
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> ---

Hi Hans,

I tested yesterday the series on a Toshiba W50-A and a Z30-A if my
memory is accurate. Internal QA tested these on a Dell M4700 (don't
know if it has a v7 touchpad though).
Both tests were conclusive, i.e. they fixes the spurious release when
right-clicking.

Tested-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

I am not confident enough with the code to review 3/4 and 4/4, but
they look sane to me.

Cheers,
Benjamin

>  drivers/input/mouse/alps.c | 28 ++++++++++++++++++++++++++++
>  1 file changed, 28 insertions(+)
>
> diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
> index d125a01..aceaade 100644
> --- a/drivers/input/mouse/alps.c
> +++ b/drivers/input/mouse/alps.c
> @@ -881,6 +881,34 @@ static void alps_get_finger_coordinate_v7(struct input_mt_pos *mt,
>                                           unsigned char *pkt,
>                                           unsigned char pkt_id)
>  {
> +       /*
> +        *       packet-fmt    b7   b6    b5   b4   b3   b2   b1   b0
> +        * Byte0 TWO & MULTI    L    1     R    M    1 Y0-2 Y0-1 Y0-0
> +        * Byte0 NEW            L    1  X1-5    1    1 Y0-2 Y0-1 Y0-0
> +        * Byte1            Y0-10 Y0-9  Y0-8 Y0-7 Y0-6 Y0-5 Y0-4 Y0-3
> +        * Byte2            X0-11    1 X0-10 X0-9 X0-8 X0-7 X0-6 X0-5
> +        * Byte3            X1-11    1  X0-4 X0-3    1 X0-2 X0-1 X0-0
> +        * Byte4 TWO        X1-10  TWO  X1-9 X1-8 X1-7 X1-6 X1-5 X1-4
> +        * Byte4 MULTI      X1-10  TWO  X1-9 X1-8 X1-7 X1-6 Y1-5    1
> +        * Byte4 NEW        X1-10  TWO  X1-9 X1-8 X1-7 X1-6    0    0
> +        * Byte5 TWO & NEW  Y1-10    0  Y1-9 Y1-8 Y1-7 Y1-6 Y1-5 Y1-4
> +        * Byte5 MULTI      Y1-10    0  Y1-9 Y1-8 Y1-7 Y1-6  F-1  F-0
> +        * L:         Left button
> +        * R / M:     Non-clickpads: Right / Middle button
> +        *            Clickpads: When > 2 fingers are down, and some fingers
> +        *            are in the button area, then the 2 coordinates reported
> +        *            are for fingers outside the button area and these report
> +        *            extra fingers being present in the right / left button
> +        *            area. Note these fingers are not added to the F field!
> +        *            so if a TWO packet is received and R = 1 then there are
> +        *            3 fingers down, etc.
> +        * TWO:       1: Two touches present, byte 0/4/5 are in TWO fmt
> +        *            0: If byte 4 bit 0 is 1, then byte 0/4/5 are in MULTI fmt
> +        *               otherwise byte 0 bit 4 must be set and byte 0/4/5 are
> +        *               in NEW fmt
> +        * F:         Number of fingers - 3, 0 means 3 fingers, 1 means 4 ...
> +        */
> +
>         mt[0].x = ((pkt[2] & 0x80) << 4);
>         mt[0].x |= ((pkt[2] & 0x3F) << 5);
>         mt[0].x |= ((pkt[3] & 0x30) >> 1);
> --
> 2.1.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v2 2/3] HID: wacom: Initialize MT slots for generic devices at post_parse_hid
From: Benjamin Tissoires @ 2014-12-05 22:00 UTC (permalink / raw)
  To: Jason Gerecke; +Cc: Jiri Kosina, linux-input, Ping Cheng
In-Reply-To: <1417815453-10618-1-git-send-email-killertofu@gmail.com>

On Fri, Dec 5, 2014 at 4:37 PM, Jason Gerecke <killertofu@gmail.com> wrote:
> If a HID descriptor places HID_DG_CONTACTID before HID_DG_X and HID_DG_Y then
> the ABS_X and ABS_Y will not be automatically initialized by the call to
> input_mt_init_slots. To ensure that this is not a problem, we relocate that
> call to occur after HID parsing has been completed and we've initalized all
> the multitouch axes.
>
> Signed-off-by: Jason Gerecke <killertofu@gmail.com>
> ---

This one is
Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

Thanks for the v2 Jason!

Cheers,
Benjamin

> Changes from v1:
>  * Patch renamed from "HID: wacom: Manually declare ABS_{X,Y} for pointer emulation"
>  * Patch moves call to input_mt_init_slots rather than manually declare ABS_{X,Y}
>
>  drivers/hid/wacom_sys.c | 18 ++++++++++++++++++
>  drivers/hid/wacom_wac.c |  3 ---
>  2 files changed, 18 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
> index eb55316..872aa0b 100644
> --- a/drivers/hid/wacom_sys.c
> +++ b/drivers/hid/wacom_sys.c
> @@ -13,6 +13,7 @@
>
>  #include "wacom_wac.h"
>  #include "wacom.h"
> +#include <linux/input/mt.h>
>
>  #define WAC_MSG_RETRIES                5
>
> @@ -236,6 +237,21 @@ static void wacom_usage_mapping(struct hid_device *hdev,
>                 wacom_wac_usage_mapping(hdev, field, usage);
>  }
>
> +static void wacom_post_parse_hid(struct hid_device *hdev,
> +                                struct wacom_features *features)
> +{
> +       struct wacom *wacom = hid_get_drvdata(hdev);
> +       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
> +
> +       if (features->type == HID_GENERIC) {
> +               /* Any last-minute generic device setup */
> +               if (features->touch_max > 1) {
> +                       input_mt_init_slots(wacom_wac->input, wacom_wac->features.touch_max,
> +                                   INPUT_MT_DIRECT);
> +               }
> +       }
> +}
> +
>  static void wacom_parse_hid(struct hid_device *hdev,
>                            struct wacom_features *features)
>  {
> @@ -270,6 +286,8 @@ static void wacom_parse_hid(struct hid_device *hdev,
>                                 wacom_usage_mapping(hdev, hreport->field[i],
>                                                 hreport->field[i]->usage + j);
>         }
> +
> +       wacom_post_parse_hid(hdev, features);
>  }
>
>  static int wacom_hid_set_device_mode(struct hid_device *hdev)
> diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
> index a8a19a5..c33f889 100644
> --- a/drivers/hid/wacom_wac.c
> +++ b/drivers/hid/wacom_wac.c
> @@ -1381,7 +1381,6 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
>  {
>         struct wacom *wacom = hid_get_drvdata(hdev);
>         struct wacom_wac *wacom_wac = &wacom->wacom_wac;
> -       struct input_dev *input = wacom_wac->input;
>         unsigned touch_max = wacom_wac->features.touch_max;
>
>         switch (usage->hid) {
> @@ -1400,8 +1399,6 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
>                                         ABS_MT_POSITION_Y, 4);
>                 break;
>         case HID_DG_CONTACTID:
> -               input_mt_init_slots(input, wacom_wac->features.touch_max,
> -                       INPUT_MT_DIRECT);
>                 break;
>         case HID_DG_INRANGE:
>                 break;
> --
> 2.1.3
>

^ permalink raw reply

* Re: [PATCH 1/2] HID: wacom - Add support for DTU-1031X
From: Jiri Kosina @ 2014-12-05 22:24 UTC (permalink / raw)
  To: Ping Cheng; +Cc: linux-input, Ping Cheng
In-Reply-To: <1417746184-12192-1-git-send-email-pingc@wacom.com>

Both applied, thanks.

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply

* Re: [PATCH 1/1] x86: Surface Pro 3 Type Cover 3
From: Jarrad Whitaker @ 2014-12-06 10:57 UTC (permalink / raw)
  To: Jiri Kosina; +Cc: Alan Wu, linux-input, linux-kernel, linux-usb
In-Reply-To: <alpine.LNX.2.00.1411052137430.29198@pobox.suse.cz>

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

Hey guys,

Sorry for the very slow reply, I broke my build dir and was in the
middle of exams. Tested successfully (keyboard works after a hotplug)
with 3.18-rc7.

Jarrad

On 6 November 2014 at 07:39, Jiri Kosina <jkosina@suse.cz> wrote:
> On Mon, 3 Nov 2014, Alan Wu wrote:
>
>> Surface Pro 3 Type Cover that works with Ubuntu (and possibly Arch) from this thread. Both trackpad and keyboard work after compiling my own kernel.
>> http://ubuntuforums.org/showthread.php?t=2231207&page=2&s=44910e0c56047e4f93dfd9fea58121ef
>>
>> Also includes Jarrad Whitaker's message which sources
>> http://winaero.com/blog/how-to-install-linux-on-surface-pro-3/
>> which he says is sourced from a Russian site
>>
>> Signed-off-by: Alan Wu <alan.c.wu@gmail.com>
>
> Adding Jarrad to CC so that he can test your patch as well.
>
> Jarrad, if you could provide your Tested-by: to Alan's patch, I'd
> appreciate it (that would mean that we've reached the right super-position
> of both the patches you guys submitted).
>
> I don't own the device, so I fully depend on what you guys submit and
> test.
>
> Thanks.
>
>> ---
>>  drivers/hid/hid-core.c          | 6 ++++++
>>  drivers/hid/hid-ids.h           | 1 +
>>  drivers/hid/hid-microsoft.c     | 2 ++
>>  drivers/hid/usbhid/hid-quirks.c | 1 +
>>  4 files changed, 10 insertions(+)
>>
>> diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
>> index 73bd9e2..e94afcc 100644
>> --- a/drivers/hid/hid-core.c
>> +++ b/drivers/hid/hid-core.c
>> @@ -702,6 +702,11 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type)
>>       if (((parser->global.usage_page << 16) == HID_UP_SENSOR) &&
>>           type == HID_COLLECTION_PHYSICAL)
>>               hid->group = HID_GROUP_SENSOR_HUB;
>> +
>> +     if (hid->vendor == USB_VENDOR_ID_MICROSOFT &&
>> +         hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 &&
>> +         hid->group == HID_GROUP_MULTITOUCH)
>> +             hid->group = HID_GROUP_GENERIC;
>>  }
>>
>>  static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)
>> @@ -1861,6 +1866,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
>>       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K) },
>>       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
>>       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
>> +     { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) },
>>       { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
>>       { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
>>       { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
>> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
>> index e23ab8b..15f32c2 100644
>> --- a/drivers/hid/hid-ids.h
>> +++ b/drivers/hid/hid-ids.h
>> @@ -648,6 +648,7 @@
>>  #define USB_DEVICE_ID_MS_SURFACE_PRO_2   0x0799
>>  #define USB_DEVICE_ID_MS_TOUCH_COVER_2   0x07a7
>>  #define USB_DEVICE_ID_MS_TYPE_COVER_2    0x07a9
>> +#define USB_DEVICE_ID_MS_TYPE_COVER_3    0x07dc
>>
>>  #define USB_VENDOR_ID_MOJO           0x8282
>>  #define USB_DEVICE_ID_RETRO_ADAPTER  0x3201
>> diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
>> index 8ba17a9..cacda43 100644
>> --- a/drivers/hid/hid-microsoft.c
>> +++ b/drivers/hid/hid-microsoft.c
>> @@ -274,6 +274,8 @@ static const struct hid_device_id ms_devices[] = {
>>               .driver_data = MS_NOGET },
>>       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500),
>>               .driver_data = MS_DUPLICATE_USAGES },
>> +     { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3),
>> +             .driver_data = MS_HIDINPUT },
>>
>>       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
>>               .driver_data = MS_PRESENTER },
>> diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
>> index 5014bb5..cebfaf2 100644
>> --- a/drivers/hid/usbhid/hid-quirks.c
>> +++ b/drivers/hid/usbhid/hid-quirks.c
>> @@ -77,6 +77,7 @@ static const struct hid_blacklist {
>>       { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
>>       { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
>>       { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
>> +     { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS },
>>       { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
>>       { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
>>       { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
>> --
>> 2.1.0
>>
>
> --
> Jiri Kosina
> SUSE Labs

[-- Attachment #2: s3.patch --]
[-- Type: text/x-patch, Size: 3484 bytes --]

Signed-off-by: Alan Wu <alan.c.wu@xxxxxxxxx>
Tested-by: Jarrad Whitaker <jarrad.whitaker@gmail.com>
---
 drivers/hid/hid-core.c          | 6 ++++++
 drivers/hid/hid-ids.h           | 1 +
 drivers/hid/hid-microsoft.c     | 2 ++
 drivers/hid/usbhid/hid-quirks.c | 1 +
 4 files changed, 10 insertions(+)

diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 73bd9e2..e94afcc 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -702,6 +702,11 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type)
 	if (((parser->global.usage_page << 16) == HID_UP_SENSOR) &&
 	    type == HID_COLLECTION_PHYSICAL)
 		hid->group = HID_GROUP_SENSOR_HUB;
+
+	if (hid->vendor == USB_VENDOR_ID_MICROSOFT &&
+	    hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 &&
+	    hid->group == HID_GROUP_MULTITOUCH)
+		hid->group = HID_GROUP_GENERIC;
 }
 
 static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)
@@ -1861,6 +1866,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index e23ab8b..15f32c2 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -648,6 +648,7 @@
 #define USB_DEVICE_ID_MS_SURFACE_PRO_2   0x0799
 #define USB_DEVICE_ID_MS_TOUCH_COVER_2   0x07a7
 #define USB_DEVICE_ID_MS_TYPE_COVER_2    0x07a9
+#define USB_DEVICE_ID_MS_TYPE_COVER_3    0x07dc
 
 #define USB_VENDOR_ID_MOJO		0x8282
 #define USB_DEVICE_ID_RETRO_ADAPTER	0x3201
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
index 8ba17a9..cacda43 100644
--- a/drivers/hid/hid-microsoft.c
+++ b/drivers/hid/hid-microsoft.c
@@ -274,6 +274,8 @@ static const struct hid_device_id ms_devices[] = {
 		.driver_data = MS_NOGET },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500),
 		.driver_data = MS_DUPLICATE_USAGES },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3),
+		.driver_data = MS_HIDINPUT },
 
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
 		.driver_data = MS_PRESENTER },
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 5014bb5..cebfaf2 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -77,6 +77,7 @@ static const struct hid_blacklist {
 	{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
 	{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
 	{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
+	{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS },
 	{ USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
 	{ USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
 	{ USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },


^ permalink raw reply related

* Re: Disable input device
From: Pavel Machek @ 2014-12-06 12:47 UTC (permalink / raw)
  To: Pali Rohár
  Cc: Dmitry Torokhov, Sebastian Reichel, kernel list, linux-input,
	Henrik Rydberg
In-Reply-To: <201411291824.03996@pali>

On Sat 2014-11-29 18:24:03, Pali Rohár wrote:
> Hello,
> 
> there is request to introduce some option (via sysfs) to disable 
> one input device. Originally it was for tsc2005 touchscreen found 
> on Nokia N900 cellphone. Use case is to completely disable 
> touchscreen when phone is in pocket or when screen is locked via 
> slide switch (to prevent random clicks).
> 
> But it would be better to have disable option for all input 
> devices. Sometimes input device is not use and only consume 
> power. Another case is when input device (e.g. keyboard or 
> touchpad) is integrated into machine (e.g. on laptops) and user 
> does not want to use it (so make sure that kernel does not send 
> events to userspace).
> 
> I would like to ask, how to solve it?
> 
> What do you think about adding new sysfs file "disable" (accept 
> values 1 or 0) for every input device? With "1" it cause that 
> kernel will drop all events from specific input device and if 
> driver provide some function is can be called (e.g. for power 
> management or disabling device at hardware level).

That makes sense to me. Dmitry, would such interface be acceptable?

									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 2/3] drivers:input:ps2 Added sunxi A20 ps2 driver, changed makefile and Kconfig
From: Vishnu Patekar @ 2014-12-06 13:31 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: linux-sunxi-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
	benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r@public.gmane.org,
	msalter-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org,
	ralf-6z/3iImG2C8G8FEW9MqTrA@public.gmane.org,
	jdelvare-l3A5Bk7waGM@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-input-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Dmitry Torokhov
In-Reply-To: <20141205100151.GT30256@lukather>

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

Hello Maxime,
Sorry for inconvenience. I'll follow the guidelines, will implement the
review comment and resubmit the patch for review.


On Fri, Dec 5, 2014 at 3:31 PM, Maxime Ripard <
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org> wrote:

> Hi,
>
> Thanks for working on this.
>
> First, make sure to read Documentation/SubmittingPatches. The
> guidelines in there might seem tedious, but it's there to enforce that
> we keep the kernel homogenous.
>
> Most notably, you should make sure that:
>   - you run scripts/checkpatch.pl on your patches
>   - you have a commit log
>   - your commit title stays below 80 chars
>   - you have a Signed-off-by in your commit log
>
> On Thu, Dec 04, 2014 at 04:23:44AM +0530, vishnupatekar wrote:
> > ---
> >  drivers/input/serio/Kconfig     |    9 ++
> >  drivers/input/serio/Makefile    |    1 +
> >  drivers/input/serio/sunxi-ps2.c |  305
> +++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 315 insertions(+)
> >  create mode 100644 drivers/input/serio/sunxi-ps2.c
> >
> > diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
> > index bc2d474..1a86e41 100644
> > --- a/drivers/input/serio/Kconfig
> > +++ b/drivers/input/serio/Kconfig
> > @@ -281,4 +281,13 @@ config HYPERV_KEYBOARD
> >         To compile this driver as a module, choose M here: the module
> will
> >         be called hyperv_keyboard.
> >
> > +config SERIO_SUNXI_PS2
> > +     tristate "Allwinner Sun7i-A20 PS/2 controller"
> > +     default m
> > +     help
> > +       Say Y here if you have Sun7i-A20 Allwinner PS/2 ports.
> > +
> > +       To compile this driver as a module, choose M here: the
> > +       module will be called sunxi-ps2.
> > +
> >  endif
> > diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
> > index 815d874..0fa0f78 100644
> > --- a/drivers/input/serio/Makefile
> > +++ b/drivers/input/serio/Makefile
> > @@ -29,3 +29,4 @@ obj-$(CONFIG_SERIO_ARC_PS2) += arc_ps2.o
> >  obj-$(CONFIG_SERIO_APBPS2)   += apbps2.o
> >  obj-$(CONFIG_SERIO_OLPC_APSP)        += olpc_apsp.o
> >  obj-$(CONFIG_HYPERV_KEYBOARD)        += hyperv-keyboard.o
> > +obj-$(CONFIG_SERIO_SUNXI_PS2)        += sunxi-ps2.o
> > diff --git a/drivers/input/serio/sunxi-ps2.c
> b/drivers/input/serio/sunxi-ps2.c
> > new file mode 100644
> > index 0000000..ccd7b29
> > --- /dev/null
> > +++ b/drivers/input/serio/sunxi-ps2.c
> > @@ -0,0 +1,305 @@
> > +/*
> > + * sunxi-ps2.c Support for Allwinner A20 PS2 host controller
> > + *
> > + * Author: Aaron.maoye <leafy.myeh-Q9AEpCAkrSgqDJ6do+/SaQ@public.gmane.org>
> > + *      Vishnu Patekar <vishnupatekar0510-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> > + * Based on sunxi-ps2.c 3.0 kernel
>
> Mentionning that it's Allwinner's 3.0 kernel would be good, it doesn't
> make much sense otherwise.
>
> Okie

> > +#include <linux/module.h>
> > +#include <linux/serio.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/errno.h>
> > +#include <linux/slab.h>
> > +#include <linux/list.h>
> > +#include <linux/io.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +
> > +#define DRIVER_NAME          "sunxi-ps2"
> > +
> > +#define RESSIZE(res)        (((res)->end - (res)->start)+1)
> > +
> > +#define SW_PS2_GCTRL        (0x00)
> > +#define SW_PS2_DATA         (0x04)
> > +#define SW_PS2_LCTRL        (0x08)
> > +#define SW_PS2_LSTAT        (0x0c)
> > +#define SW_PS2_FCTRL        (0x10)
> > +#define SW_PS2_FSTAT        (0x14)
> > +#define SW_PS2_CLKDR        (0x18)
>
> The SW_ prefix is a left over from Allwinner I suppose?
>
> Using a prefix like sun4i_ps2/sun7i_ps2 depending on the outcome of
> the discussion you had with Hans would seem better, across all your
> driver.
>
> Agreed, as this PS2 driver can be applicable for A10 and A20, I'll keep it
as PS2_REG_* for registers, e.g. PS2_REG_GCTL  and PS2_GCTL_BUSEN for bits.
it is similar to SUNXI MMC driver.

> > +
> > +/* SW_PS2_GCTRL */
> > +#define SWPS2_BUSEN         (1 << 0)
> > +#define SWPS2_MASTER        (1 << 1)
> > +#define SWPS2_RESET         (1 << 2)
> > +#define SWPS2_INTEN         (1 << 3)
>
> We usually have the convention of having the register name mentionned
> in the bits offsets, and the bits declared right after their register.
>
> Something like:
>
> #define SUN4I_PS2_GCTRL_REG     0x00
> #define SUN4I_PS2_GCTRL_BUS_EN          BIT(0)
>
> #define SUN4I_PS2_LCTRL_REG     0x08
> #define SUN4I_PS2_LCTRL_PAR_ERR_EN      BIT(1)
>
> etc....
>
> Agreed, BIT(x) macros will be used.

> > +#define SWPS2_INTFLAG       (1 << 3)
>
> This is defined to the same value than _INTEN, is this intentionnal?
>
> This macro is not used, will be removed.


> > +
> > +/* SW_PS2_LCTRL */
> > +#define SWPS2_LCTL_NOACK        (0x0 << 18)
> > +#define SWPS2_LCTL_TXDTOEN      (0x1 << 8)
> > +#define SWPS2_LCTL_STOPERREN    (0x1 << 3)
> > +#define SWPS2_LCTL_ACKERREN     (0x1 << 2)
> > +#define SWPS2_LCTL_PARERREN     (0x1 << 1)
> > +#define SWPS2_LCTL_RXDTOEN      (0x1 << 0)
> > +
> > +/* SW_PS2_FSTAT */
> > +#define SWPS2_FSTA_RXRDY        (1 << 0)
> > +#define SWPS2_FSTA_RXOF         (1 << 1)
> > +#define SWPS2_FSTA_RXUF         (1 << 2)
> > +#define SWPS2_FSTA_TXRDY        (1 << 8)
> > +#define SWPS2_FSTA_TXOF         (1 << 9)
> > +#define SWPS2_FSTA_TXUF         (1 << 10)
> > +
> > +#define SW_PS2_SAMPLE_CLK      (1000000)
> > +#define SW_PS2_SCLK            (125000)
> > +
> > +struct sunxips2data {
> > +     int irq;
> > +     spinlock_t ps2_lock;
> > +     void __iomem *base_address;     /* virt address of control
> registers*/
> > +     struct serio *serio;            /* serio*/
> > +     struct device *dev;
> > +     struct  clk     *pclk;
> > +};
> > +
> > +/*********************/
> > +/* Interrupt handler */
> > +/*********************/
> > +static irqreturn_t sunxips2_interrupt(int irq, void *dev_id)
> > +{
> > +     struct sunxips2data *drvdata = dev_id;
> > +     u32 intr_status;
> > +     u32 fifo_status;
> > +     unsigned char byte;
> > +     u32 rval;
> > +     u32 error = 0;
> > +
> > +     spin_lock(&drvdata->ps2_lock);
> > +
> > +     /* Get the PS/2 interrupts and clear them */
> > +     intr_status  = readl(drvdata->base_address + SW_PS2_LSTAT);
> > +     fifo_status  = readl(drvdata->base_address + SW_PS2_FSTAT);
> > +
> > +     /*Check Line Status Register*/
> > +     if (intr_status & 0x10f) {
> > +             if (intr_status & 0x08)
> > +                     dev_info(drvdata->dev, "PS/2 Stop Bit Error!");
> > +             if (intr_status & 0x04)
> > +                     dev_info(drvdata->dev, "PS/2 Acknowledge
> Error!\n");
> > +             if (intr_status & 0x02)
> > +                     dev_info(drvdata->dev, "PS/2 Parity Error!\n");
> > +             if (intr_status & 0x100)
> > +                     dev_info(drvdata->dev, "PS/2 Transmit Data
> Timeout!\n");
> > +             if (intr_status & 0x01)
> > +                     dev_info(drvdata->dev, "PS/2 Receive Data
> Timeout!\n");
> > +
> > +             writel(readl(drvdata->base_address + SW_PS2_GCTRL)|0x4,
> drvdata->base_address + SW_PS2_GCTRL);/*reset PS/2 controller*/
> > +             writel(0x10f, drvdata->base_address + SW_PS2_LSTAT);
> > +
> > +             error = 1;
> > +     }
> > +
> > +    /*Check FIFO Status Register*/
> > +     if (fifo_status & 0x0606) {
> > +             if (fifo_status & 0x400)
> > +                     dev_info(drvdata->dev, "PS/2 Tx FIFO
> Underflow!\n");
> > +             if (fifo_status & 0x200)
> > +                     dev_info(drvdata->dev, "PS/2 Tx FIFO Overflow!\n");
> > +             if (fifo_status & 0x04)
> > +                     dev_info(drvdata->dev, "PS/2 Rx FIFO
> Underflow!\n");
> > +             if (fifo_status & 0x02)
> > +                     dev_info(drvdata->dev, "PS/2 Rx FIFO Overflow!\n");
> > +
> > +             writel(readl(drvdata->base_address + SW_PS2_GCTRL)|0x4,
> drvdata->base_address + SW_PS2_GCTRL); /*reset PS/2 controller*/
> > +             writel(0x707, drvdata->base_address + SW_PS2_FSTAT);
> > +             error = 1;
> > +     }
> > +
> > +     rval = (fifo_status >> 16) & 0x3;
> > +     while (!error && rval--) {
> > +             byte = readl(drvdata->base_address + SW_PS2_DATA) & 0xff;
> > +             dev_info(drvdata->dev, "PS/2 Receive %02x\n", byte);
> > +             serio_interrupt(drvdata->serio, byte, 0);
> > +     }
> > +
> > +     writel(intr_status, drvdata->base_address + SW_PS2_LSTAT);
> > +     writel(fifo_status, drvdata->base_address + SW_PS2_FSTAT);
> > +
> > +     spin_unlock(&drvdata->ps2_lock);
> > +
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +static int sunxips2_open(struct serio *pserio)
> > +{
> > +     struct sunxips2data *drvdata = pserio->port_data;
> > +     u32 src_clk = 0;
> > +     u32 clk_scdf;
> > +     u32 clk_pcdf;
> > +     u32 rval;
> > +
> > +     /*Set Line Control And Enable Interrupt*/
> > +     rval =
> SWPS2_LCTL_TXDTOEN|SWPS2_LCTL_STOPERREN|SWPS2_LCTL_ACKERREN|SWPS2_LCTL_PARERREN|SWPS2_LCTL_RXDTOEN;
> > +     writel(rval, drvdata->base_address + SW_PS2_LCTRL);
> > +
> > +     /*Reset FIFO*/
> > +     writel(0x3<<16 | 0x607, drvdata->base_address + SW_PS2_FCTRL);
> > +
> > +     src_clk = clk_get_rate(drvdata->pclk);
> > +
> > +     if (!src_clk) {
> > +             dev_info(drvdata->dev, "w_ps2c_set_sclk error, source
> clock is 0.");
> > +             return -1;
> > +     }
> > +
> > +     /*Set Clock Divider Register*/
> > +     clk_scdf = ((src_clk + (SW_PS2_SAMPLE_CLK>>1)) / SW_PS2_SAMPLE_CLK
> - 1);
> > +     clk_pcdf = ((SW_PS2_SAMPLE_CLK + (SW_PS2_SCLK>>1)) / SW_PS2_SCLK -
> 1);
> > +     rval = (clk_scdf<<8) | clk_pcdf;/* | (PS2_DEBUG_SEL<<16);*/
>
> What is this supposed to do? Calculating and rounding the dividers?
>
> You should look into DIV_ROUND_* if that's so
>
> Yes, Agreed, It'll be used as below, this solves purpose.

clk_scdf = DIV_ROUND_UP(src_clk, PS2_SAMPLE_CLK) -1 ;
clk_pcdf = DIV_ROUND_UP(PS2_SAMPLE_CLK, PS2_SCLK) -1;
rval = (clk_scdf<<8) | clk_pcdf;
writel(rval, drvdata->reg_base + PS2_CLKDR);


> > +     writel(rval, drvdata->base_address + SW_PS2_CLKDR);
> > +
> > +     /*Set Global Control Register*/
> > +     rval = SWPS2_RESET|SWPS2_INTEN|SWPS2_MASTER|SWPS2_BUSEN;
> > +     writel(rval, drvdata->base_address + SW_PS2_GCTRL);
> > +
> > +     udelay(100);
>
> Why is that udelay needed?
>
> No need, will be removed.

> > +
> > +     return 0;
> > +}
> > +
> > +static void sunxips2_close(struct serio *pserio)
> > +{
> > +     struct sunxips2data *drvdata = pserio->port_data;
> > +
> > +     spin_lock(&drvdata->ps2_lock);
>
> spin_lock_irqsave would be better I guess.
>
> I'll use it.

> > +     /* Disable the PS2 interrupts */
> > +     writel(0, drvdata->base_address + SW_PS2_GCTRL);
> > +     spin_unlock(&drvdata->ps2_lock);
> > +}
> > +
> > +static int sunxips2_write(struct serio *pserio, unsigned char val)
> > +{
> > +     struct sunxips2data *drvdata = (struct sunxips2data
> *)pserio->port_data;
> > +     u32 timeout = 10000;
> > +
> > +     do {
> > +             if (readl(drvdata->base_address + SW_PS2_FSTAT) &
> SWPS2_FSTA_TXRDY) {
> > +                     writel(val, drvdata->base_address + SW_PS2_DATA);
> > +                     return 0;
> > +             }
> > +     } while (timeout--);
>
> Please use time_before() here
>
> Agreed, time_before(jiffies, expire) will be used.

> > +     return -1;
>
> And return a meaningful error.
>
> Okie

> > +static int sunxips2_probe(struct platform_device *ofdev)
>
> The ofdev name is measleading. This is a platform_device structure, it
> could be probed by other mechanisms than OF, so please use a different
> name here. Usually pdev is used.
>
> > +{
> > +     struct resource *res; /* IO mem resources */
> > +     struct sunxips2data *drvdata;
> > +     struct serio *serio;
> > +     struct device *dev = &ofdev->dev;
> > +     unsigned int irq;
> > +     int error;
> > +
> > +     drvdata = devm_kzalloc(dev, sizeof(struct sunxips2data),
> GFP_KERNEL);
> > +     serio = devm_kzalloc(dev, sizeof(struct serio), GFP_KERNEL);
> > +     if (!drvdata || !serio)
> > +             error = -ENOMEM;
> > +
> > +     /* Request clock */
> > +     drvdata->pclk = clk_get(dev, NULL);
>
> You can use devm_clk_get.
>
> Agreed, devm_clk_get will be used.

> > +     if (IS_ERR(drvdata->pclk))
> > +             dev_dbg(dev, "couldn't get clock %li\n",
> > +                     PTR_ERR(drvdata->pclk));
>
> This is an error, you should treat it as such.
>
> > +     if (!IS_ERR(drvdata->pclk)) {
> > +             error = clk_prepare_enable(drvdata->pclk);
> > +             if (error < 0) {
> > +                     dev_err(dev, "failed to enable clock %d\n", error);
> > +                     return error;
> > +             }
> > +     }
> > +
> > +     /* IO */
> > +     res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
> > +     drvdata->base_address = devm_ioremap_resource(dev, res);
> > +     if (IS_ERR(drvdata->base_address)) {
> > +             dev_err(dev, "failed to map registers\n");
> > +             error = PTR_ERR(drvdata->base_address);
> > +     }
>
> ditto.
>
> > +     serio->id.type = SERIO_8042;
> > +     serio->write = sunxips2_write;
> > +     serio->open = sunxips2_open;
> > +     serio->close = sunxips2_close;
> > +     serio->port_data = drvdata;
> > +     serio->dev.parent = dev;
> > +     strlcpy(serio->name, dev_name(dev), sizeof(serio->name));
> > +     strlcpy(serio->phys, dev_name(dev), sizeof(serio->phys));
> > +
> > +     platform_set_drvdata(ofdev, drvdata);
> > +     serio_register_port(serio);
> > +
> > +     /* Get IRQ for the device */
> > +     irq = irq_of_parse_and_map(ofdev->dev.of_node, 0);
> > +     if (!irq) {
> > +             dev_err(dev, "no IRQ found\n");
> > +             return -ENODEV;
> > +     }
>
> You can use platform_get_irq, for consistency with how you retrieve
> resources.
>
> You're also no unregistering the driver from the serio framework.
>
> I'll put code for error handling and freeing allocated resources.

> > +     drvdata->irq = irq;
> > +     drvdata->serio = serio;
> > +     drvdata->dev = dev;
> > +     error = devm_request_any_context_irq(drvdata->dev, drvdata->irq,
> &sunxips2_interrupt, 0,
> > +                             DRIVER_NAME, drvdata);
>
> request_irq is enough here.
>
> Okie, I'll use devm_request_threaded_irq as request_irq is just wrapper
arroud it.

> > +     if (error) {
> > +             dev_err(drvdata->dev,
> > +                     "Couldn't allocate interrupt %d : error: %d\n",
> drvdata->irq, error);
> > +             return error;
> > +     }
> > +     return 0;               /* success */
> > +}
> > +
> > +static int sunxips2_remove(struct platform_device *of_dev)
> > +{
> > +     struct sunxips2data *drvdata = platform_get_drvdata(of_dev);
> > +
> > +     if (!IS_ERR(drvdata->pclk)) {
> > +             clk_disable_unprepare(drvdata->pclk);
> > +             clk_put(drvdata->pclk);
> > +     }
> > +     serio_unregister_port(drvdata->serio);
> > +     mdelay(2);
>
> Why a mdelay?
>
No need, will be removed.

>
> > +     return 0;
> > +}
> > +
> > +/* Match table for of_platform binding */
> > +static const struct of_device_id sunxips2_of_match[] = {
> > +     { .compatible = "allwinner,sun7i-a20-ps2", },
> > +     { },
> > +};
> > +
> > +MODULE_DEVICE_TABLE(of, sunxips2_of_match);
> > +
> > +/*platform driver structure*/
> > +static struct platform_driver sunxips2_of_driver = {
> > +     .probe          = sunxips2_probe,
> > +     .remove         = sunxips2_remove,
> > +     .driver = {
> > +             .name = DRIVER_NAME,
> > +             .owner = THIS_MODULE,
>
> You can drop the owner field, it's already set by
> module_platform_driver.
>
> Okie

> > +             .of_match_table = sunxips2_of_match,
> > +     },
> > +};
> > +module_platform_driver(sunxips2_of_driver);
> > +
> > +MODULE_AUTHOR("Aaron.maoye<leafy.myeh-Q9AEpCAkrSgqDJ6do+/SaQ@public.gmane.org, "
> > +             "Vishnu Patekar <vishnupatekar0510-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>");
>
> Usually, you define two MODULE_AUTHOR.
>
> Okie

> Maxime
>
> --
> Maxime Ripard, Free Electrons
> Embedded Linux, Kernel and Android engineering
> http://free-electrons.com
>

-- 
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.

[-- Attachment #2: Type: text/html, Size: 25940 bytes --]

^ permalink raw reply

* Re: Disable input device
From: Chuck Ebbert @ 2014-12-06 15:28 UTC (permalink / raw)
  To: Pali Rohár
  Cc: Dmitry Torokhov, Sebastian Reichel, Pavel Machek, kernel list,
	linux-input, Henrik Rydberg
In-Reply-To: <201411291824.03996@pali>

On Sat, 29 Nov 2014 18:24:03 +0100
Pali Rohár <pali.rohar@gmail.com> wrote:

> What do you think about adding new sysfs file "disable" (accept 
> values 1 or 0) for every input device? With "1" it cause that 
> kernel will drop all events from specific input device and if 
> driver provide some function is can be called (e.g. for power 
> management or disabling device at hardware level).
> 

Yeah, I'd like to see this too. I am using xinput to disable the
notebook keyboard so the cats walking across it don't cause any
problems. It would be nice to have a better solution.
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH] Input: gpio_keys - initialize the 'irq' variable
From: Fabio Estevam @ 2014-12-06 15:43 UTC (permalink / raw)
  To: dmitry.torokhov; +Cc: alexander.stein, linux-input, Fabio Estevam

From: Fabio Estevam <fabio.estevam@freescale.com>

Commit f2d347ff70be453e ("Input: gpio_keys - add device tree support for
interrupt only keys") caused the following build warning:

drivers/input/keyboard/gpio_keys.c: In function 'gpio_keys_probe':
drivers/input/keyboard/gpio_keys.c:647:15: warning: 'irq' may be used uninitialized in this function [-Wmaybe-uninitialized]
drivers/input/keyboard/gpio_keys.c:622:7: note: 'irq' was declared here

Initialize 'irq' with a default value to fix it.

Reported-by: Olof's autobuilder <build@lixom.net>
Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com>
---
 drivers/input/keyboard/gpio_keys.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index aa13055..70e89d1 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -619,7 +619,7 @@ gpio_keys_get_devtree_pdata(struct device *dev)
 	i = 0;
 	for_each_child_of_node(node, pp) {
 		int gpio = -1;
-		int irq;
+		int irq = -EINVAL;
 		enum of_gpio_flags flags;
 
 		if (!of_find_property(pp, "gpios", NULL)) {
-- 
1.9.1


^ permalink raw reply related

* [PATCHv2 0/3] ARM:sunxi:ps2 Added support for A10/A20 ps2 controller.
From: vishnupatekar @ 2014-12-06 23:15 UTC (permalink / raw)
  To: linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	linux-lFZ/pmaqli7XmaaqVzeoHQ
  Cc: leafy.myeh-Q9AEpCAkrSgqDJ6do+/SaQ, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA, vishnupatekar

Here is v2 of SUNXI PS2 controller support patch-set as with v1.

Changes in v2: 
1. added default n depends on ARCH_SUNXI || COMPILE_TEST in Kconfig.
2. handled errors and free resources on errors.
3. used BIT(x), DIV_ROUND_UP macros.
4. corrected style errors.
5. added support for A10 also, A10 and A2 have same properties of PS2 controller.
6. by default commented ps20 and ps21 nodes,as ps20 pins conflict with HDMI
   connector on Lime2 Board.
7. added compatible as allwinner,sun4i-a10-ps2.
8. corrected the possible race condition.

Patch 0 Summary: Allwinner A10/A20 PS2 controller. These modifications are
	for PS2 host controller. IBM compliant IBM PS2 and AT-compatible 
	keyboard and mouse can be connected.

Patch 1 device tree bindings.

Patch 2 adds support for sun7i ps2 driver.

Patch 3 device tree support for PS2 controller.
 1) Added A10/A20 ps2 nodes to the dtsi
 2) Added A10/A20 ps2 pinmux to the dtsi
 3) Added ps2 nodes to the lime2 dts file

vishnupatekar (3):
  sunxi:dts-bindings:input:ps2 bindings for A10/A20 ps2.
  sunxi:drivers:input:ps2 Added sunxi A10/A20 ps2 driver
  ARM:dts:sunxi:ps2 dt nodes for A10/A20 PS2 controller.

 .../bindings/input/allwinner,sunxi-ps2.txt         |   23 ++
 arch/arm/boot/dts/sun4i-a10.dtsi                   |   27 ++
 arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts    |   13 +
 arch/arm/boot/dts/sun7i-a20.dtsi                   |   29 ++
 drivers/input/serio/Kconfig                        |   10 +
 drivers/input/serio/Makefile                       |    1 +
 drivers/input/serio/sunxi-ps2.c                    |  364 ++++++++++++++++++++
 7 files changed, 467 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/allwinner,sunxi-ps2.txt
 create mode 100644 drivers/input/serio/sunxi-ps2.c

-- 
1.7.9.5

^ permalink raw reply

* [PATCHv2 1/3] sunxi:dts-bindings:input:ps2 bindings for A10/A20 ps2.
From: vishnupatekar @ 2014-12-06 23:15 UTC (permalink / raw)
  To: linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	linux-lFZ/pmaqli7XmaaqVzeoHQ
  Cc: leafy.myeh-Q9AEpCAkrSgqDJ6do+/SaQ, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA, vishnupatekar
In-Reply-To: <1417907719-26775-1-git-send-email-VishnuPatekar0510-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

A10 and A20 have same PS2 addresses, clocks, interrupts.
added compatible as allwinner,sun4i-a10-ps2.

Signed-off-by: vishnupatekar <VishnuPatekar0510-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
 .../bindings/input/allwinner,sunxi-ps2.txt         |   23 ++++++++++++++++++++
 1 file changed, 23 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/allwinner,sunxi-ps2.txt

diff --git a/Documentation/devicetree/bindings/input/allwinner,sunxi-ps2.txt b/Documentation/devicetree/bindings/input/allwinner,sunxi-ps2.txt
new file mode 100644
index 0000000..3a8919a
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/allwinner,sunxi-ps2.txt
@@ -0,0 +1,23 @@
+* Device tree bindings for Allwinner A10, A20 PS2 host controller
+
+A20 PS2 is dual role controller(PS2 host and PS2 device). These bindings are for PS2 host controller.
+IBM compliant IBM PS2 and AT-compatible keyboard and mouse can be connected.
+
+Required properties:
+
+ - reg             : Offset and length of the register set for the device.
+ - compatible      : Should one of the following:
+                     - "allwinner,sun7i-a20-ps2"
+                     - "allwinner,sun4i-a10-ps2"
+ - interrupts      : The interrupt line connected to the PS2.
+ - clocks          : The gate clk connected to the PS2.
+
+
+Example:
+	ps20: ps2@0x01c2a000 {
+		compatible = "allwinner,sun7i-a20-ps2";
+		reg = <0x01c2a000 0x400>;
+		interrupts = <0 62 4>;
+		clocks = <&apb1_gates 6>;
+		status = "disabled";
+	};
-- 
1.7.9.5

^ permalink raw reply related

* [PATCHv2 2/3] sunxi:drivers:input:ps2 Added sunxi A10/A20 ps2 driver
From: vishnupatekar @ 2014-12-06 23:15 UTC (permalink / raw)
  To: linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	linux-lFZ/pmaqli7XmaaqVzeoHQ
  Cc: leafy.myeh-Q9AEpCAkrSgqDJ6do+/SaQ, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA, vishnupatekar
In-Reply-To: <1417907719-26775-1-git-send-email-VishnuPatekar0510-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

 -added compatible as allwinner,sun4i-a10-ps2 and allwinner,sun7i-a20-ps2.
 - added default n depends on ARCH_SUNXI || COMPILE_TEST
 in Kconfig.
 -handled errors and free resources on errors.
 -used BIT(x), DIV_ROUND_UP macros.
 -corrected style errors.

Signed-off-by: vishnupatekar <VishnuPatekar0510-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
 drivers/input/serio/Kconfig     |   10 ++
 drivers/input/serio/Makefile    |    1 +
 drivers/input/serio/sunxi-ps2.c |  364 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 375 insertions(+)
 create mode 100644 drivers/input/serio/sunxi-ps2.c

diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index bc2d474..3a7599c 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -281,4 +281,14 @@ config HYPERV_KEYBOARD
 	  To compile this driver as a module, choose M here: the module will
 	  be called hyperv_keyboard.
 
+config SERIO_SUNXI_PS2
+	tristate "Allwinner Sun4i-A10/Sun7i-A20 PS/2 controller"
+	default n
+	depends on ARCH_SUNXI || COMPILE_TEST
+	help
+	  Say Y here if you have Sun4i-A10/Sun7i-A20 Allwinner PS/2 ports.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sunxi-ps2.
+
 endif
diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
index 815d874..0fa0f78 100644
--- a/drivers/input/serio/Makefile
+++ b/drivers/input/serio/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_SERIO_ARC_PS2)	+= arc_ps2.o
 obj-$(CONFIG_SERIO_APBPS2)	+= apbps2.o
 obj-$(CONFIG_SERIO_OLPC_APSP)	+= olpc_apsp.o
 obj-$(CONFIG_HYPERV_KEYBOARD)	+= hyperv-keyboard.o
+obj-$(CONFIG_SERIO_SUNXI_PS2)	+= sunxi-ps2.o
diff --git a/drivers/input/serio/sunxi-ps2.c b/drivers/input/serio/sunxi-ps2.c
new file mode 100644
index 0000000..4cd89ae
--- /dev/null
+++ b/drivers/input/serio/sunxi-ps2.c
@@ -0,0 +1,364 @@
+/*
+ *	Driver for Allwinner A20 PS2 host controller
+ *
+ *	Author: Vishnu Patekar <vishnupatekar0510-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *		Aaron.maoye <leafy.myeh-Q9AEpCAkrSgqDJ6do+/SaQ@public.gmane.org>
+ *
+ *		Based on 3.0 kernel
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/serio.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+
+#define DRIVER_NAME		"sunxi-ps2"
+
+/* register offset definitions */
+#define PS2_REG_GCTL		(0x00)	/*  PS2 Module Global Control Reg */
+#define PS2_REG_DATA		(0x04)  /*  PS2 Module Data Reg		*/
+#define PS2_REG_LCTL		(0x08)	/*  PS2 Module Line Control Reg */
+#define PS2_REG_LSTS		(0x0C)  /*  PS2 Module Line Status Reg	*/
+#define PS2_REG_FCTL		(0x10)	/*  PS2 Module FIFO Control Reg */
+#define PS2_REG_FSTS		(0x14)	/*  PS2 Module FIFO Status Reg	*/
+#define PS2_REG_CLKDR		(0x18)	/*  PS2 Module Clock Divider Reg*/
+
+/*  PS2 GLOBAL CONTROL REGISTER PS2_GCTL */
+#define PS2_GCTL_INTFLAG	BIT(4)
+#define PS2_GCTL_INTEN		BIT(3)
+#define PS2_GCTL_RESET		BIT(2)
+#define PS2_GCTL_MASTER		BIT(1)
+#define PS2_GCTL_BUSEN		BIT(0)
+
+/* PS2 LINE CONTROL REGISTER */
+#define PS2_LCTL_NOACK		BIT(18)
+#define PS2_LCTL_TXDTOEN	BIT(8)
+#define PS2_LCTL_STOPERREN	BIT(3)
+#define PS2_LCTL_ACKERREN	BIT(2)
+#define PS2_LCTL_PARERREN	BIT(1)
+#define PS2_LCTL_RXDTOEN	BIT(0)
+
+/* PS2 LINE STATUS REGISTER */
+#define PS2_LSTS_TXTDO		BIT(8)
+#define PS2_LSTS_STOPERR	BIT(3)
+#define PS2_LSTS_ACKERR		BIT(2)
+#define PS2_LSTS_PARERR		BIT(1)
+#define PS2_LSTS_RXTDO		BIT(0)
+
+/* PS2 FIFO CONTROL REGISTER */
+#define PS2_FCTL_TXRST		BIT(17)
+#define PS2_FCTL_RXRST		BIT(16)
+#define PS2_FCTL_TXUFIEN	BIT(10)
+#define PS2_FCTL_TXOFIEN	BIT(9)
+#define PS2_FCTL_TXRDYIEN	BIT(8)
+#define PS2_FCTL_RXUFIEN	BIT(2)
+#define PS2_FCTL_RXOFIEN	BIT(1)
+#define PS2_FCTL_RXRDYIEN	BIT(0)
+
+/* PS2 FIFO STATUS REGISTER */
+#define PS2_FSTS_TXUF		BIT(10)
+#define PS2_FSTS_TXOF		BIT(9)
+#define PS2_FSTS_TXRDY		BIT(8)
+#define PS2_FSTS_RXUF		BIT(2)
+#define PS2_FSTS_RXOF		BIT(1)
+#define PS2_FSTS_RXRDY		BIT(0)
+
+
+#define PS2_LINE_ERROR_BIT \
+	(PS2_LSTS_TXTDO|PS2_LSTS_STOPERR|PS2_LSTS_ACKERR| \
+	PS2_LSTS_PARERR|PS2_LSTS_RXTDO)
+
+#define PS2_FIFO_ERROR_BIT \
+	(PS2_FSTS_TXUF|PS2_FSTS_TXOF|PS2_FSTS_TXRDY|PS2_FSTS_RXUF| \
+	PS2_FSTS_RXOF|PS2_FSTS_RXRDY)
+
+#define PS2_SAMPLE_CLK		(1000000)
+#define PS2_SCLK		(125000)
+
+struct sunxips2data {
+	struct serio *serio;
+	struct device *dev;
+
+	/* IO mapping base */
+	void __iomem	*reg_base;
+
+	/* clock management */
+	struct clk	*clk;
+
+	/* irq */
+	spinlock_t	lock;
+	int		irq;
+};
+
+/*********************/
+/* Interrupt handler */
+/*********************/
+static irqreturn_t sunxips2_interrupt(int irq, void *dev_id)
+{
+	struct sunxips2data *drvdata = dev_id;
+	u32 intr_status;
+	u32 fifo_status;
+	unsigned char byte;
+	u32 rval;
+	u32 error = 0;
+
+	spin_lock(&drvdata->lock);
+
+	/* Get the PS/2 interrupts and clear them */
+	intr_status  = readl(drvdata->reg_base + PS2_REG_LSTS);
+	fifo_status  = readl(drvdata->reg_base + PS2_REG_FSTS);
+
+	/*Check Line Status Register*/
+	if (intr_status & 0x10f) {
+		if (intr_status & PS2_LSTS_STOPERR)
+			dev_info(drvdata->dev, "PS/2 Stop Bit Error!");
+		if (intr_status & PS2_LSTS_ACKERR)
+			dev_info(drvdata->dev, "PS/2 Acknowledge Error!\n");
+		if (intr_status & PS2_LSTS_PARERR)
+			dev_info(drvdata->dev, "PS/2 Parity Error!\n");
+		if (intr_status & PS2_LSTS_TXTDO)
+			dev_info(drvdata->dev, "PS/2 Transmit Data Timeout!\n");
+		if (intr_status & PS2_LSTS_RXTDO)
+			dev_info(drvdata->dev, "PS/2 Receive Data Timeout!\n");
+
+		/*reset PS/2 controller*/
+		writel(readl(drvdata->reg_base + PS2_REG_GCTL) | PS2_GCTL_RESET,
+			drvdata->reg_base + PS2_REG_GCTL);
+
+		rval = PS2_LSTS_TXTDO | PS2_LSTS_STOPERR | PS2_LSTS_ACKERR |
+			PS2_LSTS_PARERR | PS2_LSTS_RXTDO;
+		writel(rval, drvdata->reg_base + PS2_REG_LSTS);
+		error = 1;
+	}
+
+	/*Check FIFO Status Register*/
+	if (fifo_status & 0x0606) {
+		if (fifo_status & PS2_FSTS_TXUF)
+			dev_info(drvdata->dev, "PS/2 Tx FIFO Underflow!\n");
+		if (fifo_status & PS2_FSTS_TXOF)
+			dev_info(drvdata->dev, "PS/2 Tx FIFO Overflow!\n");
+		if (fifo_status & PS2_FSTS_RXUF)
+			dev_info(drvdata->dev, "PS/2 Rx FIFO Underflow!\n");
+		if (fifo_status & PS2_FSTS_RXOF)
+			dev_info(drvdata->dev, "PS/2 Rx FIFO Overflow!\n");
+		/*reset PS/2 controller*/
+		writel(readl(drvdata->reg_base + PS2_REG_GCTL) | PS2_GCTL_RESET,
+			drvdata->reg_base + PS2_REG_GCTL);
+
+		rval = PS2_FSTS_TXUF | PS2_FSTS_TXOF | PS2_FSTS_TXRDY |
+			PS2_FSTS_RXUF | PS2_FSTS_RXOF | PS2_FSTS_RXRDY;
+		writel(rval, drvdata->reg_base + PS2_REG_FSTS);
+		error = 1;
+	}
+
+	rval = (fifo_status >> 16) & 0x3;
+	while (!error && rval--) {
+		byte = readl(drvdata->reg_base + PS2_REG_DATA) & 0xff;
+		/* dev_info(drvdata->dev, "PS/2 Receive %02x\n", byte); */
+		serio_interrupt(drvdata->serio, byte, 0);
+	}
+
+	writel(intr_status, drvdata->reg_base + PS2_REG_LSTS);
+	writel(fifo_status, drvdata->reg_base + PS2_REG_FSTS);
+
+	spin_unlock(&drvdata->lock);
+
+	return IRQ_HANDLED;
+}
+
+
+static int sunxips2_open(struct serio *pserio)
+{
+	struct sunxips2data *drvdata = pserio->port_data;
+	u32 src_clk = 0;
+	u32 clk_scdf;
+	u32 clk_pcdf;
+	u32 rval;
+
+	/*Set Line Control And Enable Interrupt*/
+	rval = PS2_LCTL_TXDTOEN | PS2_LCTL_STOPERREN | PS2_LCTL_ACKERREN
+		| PS2_LCTL_PARERREN | PS2_LCTL_RXDTOEN;
+	writel(rval, drvdata->reg_base + PS2_REG_LCTL);
+
+	/*Reset FIFO*/
+	rval = PS2_FCTL_TXRST|PS2_FCTL_RXRST|PS2_FCTL_TXUFIEN|PS2_FCTL_TXOFIEN
+		|PS2_FCTL_RXUFIEN|PS2_FCTL_RXOFIEN|PS2_FCTL_RXRDYIEN;
+	writel(rval, drvdata->reg_base + PS2_REG_FCTL);
+
+	src_clk = clk_get_rate(drvdata->clk);
+
+	if (!src_clk) {
+		dev_info(drvdata->dev, "w_ps2c_set_sclk error, source clock is 0.");
+		return -1;
+	}
+
+	/*Set Clock Divider Register*/
+	clk_scdf = DIV_ROUND_UP(src_clk, PS2_SAMPLE_CLK) - 1;
+	clk_pcdf = DIV_ROUND_UP(PS2_SAMPLE_CLK, PS2_SCLK) - 1;
+	rval = (clk_scdf<<8) | clk_pcdf;
+	writel(rval, drvdata->reg_base + PS2_REG_CLKDR);
+
+	/*Set Global Control Register*/
+	rval = PS2_GCTL_RESET | PS2_GCTL_INTEN | PS2_GCTL_MASTER
+		| PS2_GCTL_BUSEN;
+	writel(rval, drvdata->reg_base + PS2_REG_GCTL);
+
+	return 0;
+}
+
+static void sunxips2_close(struct serio *pserio)
+{
+	struct sunxips2data *drvdata = pserio->port_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&drvdata->lock, flags);
+	/* Disable the PS2 interrupts */
+	writel(0, drvdata->reg_base + PS2_REG_GCTL);
+	spin_unlock_irqrestore(&drvdata->lock, flags);
+}
+
+static int sunxips2_write(struct serio *pserio, unsigned char val)
+{
+	unsigned long expire = jiffies + msecs_to_jiffies(10000);
+	struct sunxips2data *drvdata = (struct sunxips2data *)pserio->port_data;
+
+	do {
+		if (readl(drvdata->reg_base + PS2_REG_FSTS) & PS2_FSTS_TXRDY) {
+			writel(val, drvdata->reg_base + PS2_REG_DATA);
+			return 0;
+		}
+	} while (time_before(jiffies, expire));
+
+	return 0;
+}
+
+static int sunxips2_probe(struct platform_device *pdev)
+{
+	struct resource *res; /* IO mem resources */
+	struct sunxips2data *drvdata;
+	struct serio *serio;
+	struct device *dev = &pdev->dev;
+	unsigned int irq;
+	int error;
+
+	drvdata = devm_kzalloc(dev, sizeof(struct sunxips2data), GFP_KERNEL);
+	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!drvdata || !serio) {
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	spin_lock_init(&drvdata->lock);
+
+	/* IO */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	drvdata->reg_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(drvdata->reg_base)) {
+		dev_err(dev, "failed to map registers\n");
+		error = PTR_ERR(drvdata->reg_base);
+		goto err_free_mem;
+	}
+
+	drvdata->clk = devm_clk_get(dev, NULL);
+	if (!IS_ERR(drvdata->clk)) {
+		error = clk_prepare_enable(drvdata->clk);
+		if (error < 0) {
+			dev_err(dev, "failed to enable clock %d\n", error);
+			goto err_free_mem;
+		}
+	} else {
+		error = PTR_ERR(drvdata->clk);
+		dev_err(dev, "couldn't get clock %d\n", error);
+		goto err_free_mem;
+	}
+
+	serio->id.type = SERIO_8042;
+	serio->write = sunxips2_write;
+	serio->open = sunxips2_open;
+	serio->close = sunxips2_close;
+	serio->port_data = drvdata;
+	serio->dev.parent = dev;
+	strlcpy(serio->name, dev_name(dev), sizeof(serio->name));
+	strlcpy(serio->phys, dev_name(dev), sizeof(serio->phys));
+
+	/* Get IRQ for the device */
+	irq = platform_get_irq(pdev, 0);
+	if (!irq) {
+		dev_err(dev, "no IRQ found\n");
+		error = -ENXIO;
+		goto error_disable_clk;
+	}
+
+	drvdata->irq = irq;
+	drvdata->serio = serio;
+	drvdata->dev = dev;
+	error = devm_request_threaded_irq(dev, drvdata->irq,
+		sunxips2_interrupt, NULL, 0, DRIVER_NAME, drvdata);
+
+	if (error) {
+		dev_err(drvdata->dev, "Interrupt alloc failed %d:error:%d\n",
+			drvdata->irq, error);
+		goto error_disable_clk;
+	}
+
+	serio_register_port(serio);
+	platform_set_drvdata(pdev, drvdata);
+
+	return 0;	/* success */
+
+error_disable_clk:
+	clk_disable_unprepare(drvdata->clk);
+
+err_free_mem:
+	kfree(serio);
+	return error;
+}
+
+static int sunxips2_remove(struct platform_device *pdev)
+{
+	struct sunxips2data *drvdata = platform_get_drvdata(pdev);
+
+	serio_unregister_port(drvdata->serio);
+	disable_irq(drvdata->irq);
+
+	if (!IS_ERR(drvdata->clk))
+		clk_disable_unprepare(drvdata->clk);
+	kfree(drvdata->serio);
+
+	return 0;
+}
+
+/* Match table for of_platform binding */
+static const struct of_device_id sunxips2_of_match[] = {
+	{ .compatible = "allwinner,sun7i-a20-ps2", },
+	{ .compatible = "allwinner,sun4i-a10-ps2", },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(of, sunxips2_of_match);
+
+/*platform driver structure*/
+static struct platform_driver sunxips2_of_driver = {
+	.probe		= sunxips2_probe,
+	.remove		= sunxips2_remove,
+	.driver = {
+		.name = DRIVER_NAME,
+		.of_match_table = sunxips2_of_match,
+	},
+};
+module_platform_driver(sunxips2_of_driver);
+
+MODULE_AUTHOR("Vishnu Patekar <vishnupatekar0510-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>");
+MODULE_AUTHOR("Aaron.maoye <leafy.myeh-Q9AEpCAkrSgqDJ6do+/SaQ@public.gmane.org>");
+MODULE_DESCRIPTION("Sunxi PS/2 driver");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5

^ permalink raw reply related

* [PATCHv2 3/3]  ARM:dts:sunxi:ps2 dt nodes for A10/A20 PS2 controller.
From: vishnupatekar @ 2014-12-06 23:15 UTC (permalink / raw)
  To: linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	linux-lFZ/pmaqli7XmaaqVzeoHQ
  Cc: leafy.myeh-Q9AEpCAkrSgqDJ6do+/SaQ, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA, vishnupatekar
In-Reply-To: <1417907719-26775-1-git-send-email-VishnuPatekar0510-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

 Added ps2 nodes in lime2 board dts. By default ps20 and ps21 nodes are
 commented as ps20 pins conflict with HDMI connector
 on Lime2 Board.

Signed-off-by: vishnupatekar <VishnuPatekar0510-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
 arch/arm/boot/dts/sun4i-a10.dtsi                |   27 +++++++++++++++++++++
 arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts |   13 ++++++++++
 arch/arm/boot/dts/sun7i-a20.dtsi                |   29 +++++++++++++++++++++++
 3 files changed, 69 insertions(+)

diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi
index 5e2ec2d..4726e8d 100644
--- a/arch/arm/boot/dts/sun4i-a10.dtsi
+++ b/arch/arm/boot/dts/sun4i-a10.dtsi
@@ -615,6 +615,19 @@
 				allwinner,drive = <0>;
 				allwinner,pull = <0>;
 			};
+			ps2_0_pins: ps2_0@0 {
+				    allwinner,pins = "PI20","PI21";
+				    allwinner,function = "ps2";
+				    allwinner,drive = <0>;
+				    allwinner,pull = <0>;
+			};
+			ps2_1_pins: ps2_1@0 {
+				    allwinner,pins = "PH12","PH13";
+				    allwinner,function = "ps2";
+				    allwinner,drive = <0>;
+				    allwinner,pull = <0>;
+			};
+
 		};
 
 		timer@01c20c00 {
@@ -781,5 +794,19 @@
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
+		ps20: ps2@0x01c2a000 {
+			compatible = "allwinner,sun4i-a10-ps2";
+			reg = <0x01c2a000 0x400>;
+			interrupts = <0 62 4>;
+			clocks = <&apb1_gates 6>;
+			status = "disabled";
+		};
+		ps21: ps2@0x01c2a400 {
+			compatible = "allwinner,sun4i-a10-ps2";
+			reg = <0x01c2a400 0x400>;
+			interrupts = <0 63 4>;
+			clocks = <&apb1_gates 7>;
+			status = "disabled";
+		};
 	};
 };
diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts
index ed364d5..5624e63 100644
--- a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts
+++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts
@@ -112,6 +112,19 @@
 			pinctrl-0 = <&uart0_pins_a>;
 			status = "okay";
 		};
+		/* PS2 0 and PS2 1 are disabled by default
+		To enable PS2 0 and PS2 1 uncomment below ps20 and ps21 nodes
+		Please note that ps20 pins conflict with HDMI on Lime2 Board*/
+		/*ps20: ps2@0x01c2a000 {
+			pinctrl-names = "default";
+			pinctrl-0 = <&ps2_0_pins>;
+			status = "okay";
+		};
+		ps21: ps2@0x01c2a400 {
+			pinctrl-names = "default";
+			pinctrl-0 = <&ps2_1_pins>;
+			status = "okay";
+		};*/
 
 		i2c0: i2c@01c2ac00 {
 			pinctrl-names = "default";
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 8605f2b..96fe37d 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -852,6 +852,19 @@
 				    allwinner,drive = <0>;
 				    allwinner,pull = <0>;
 			};
+			ps2_0_pins: ps2_0@0 {
+				    allwinner,pins = "PI20","PI21";
+				    allwinner,function = "ps2";
+				    allwinner,drive = <0>;
+				    allwinner,pull = <0>;
+			};
+			ps2_1_pins: ps2_1@0 {
+				    allwinner,pins = "PH12","PH13";
+				    allwinner,function = "ps2";
+				    allwinner,drive = <0>;
+				    allwinner,pull = <0>;
+			};
+
 		};
 
 		timer@01c20c00 {
@@ -1079,5 +1092,21 @@
 			#interrupt-cells = <3>;
 			interrupts = <1 9 0xf04>;
 		};
+		ps20: ps2@0x01c2a000 {
+			compatible = "allwinner,sun7i-a20-ps2";
+			reg = <0x01c2a000 0x400>;
+			interrupts = <0 62 4>;
+			clocks = <&apb1_gates 6>;
+			status = "disabled";
+		};
+		ps21: ps2@0x01c2a400 {
+			compatible = "allwinner,sun7i-a20-ps2";
+			reg = <0x01c2a400 0x400>;
+			interrupts = <0 63 4>;
+			clocks = <&apb1_gates 7>;
+			status = "disabled";
+		};
+
+
 	};
 };
-- 
1.7.9.5

^ permalink raw reply related

* PROBLEM: [HP Stream Notebook - 11-d010nr] clickpad malfunctions after performing a hardware click
From: Amordea Whiteoak @ 2014-12-07 22:50 UTC (permalink / raw)
  To: linux-input

Original Bug Report:
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1398533

Bug Description: After clicking once at the bottom of my clickpad on
either button, if I move my cursor elsewhere on the screen, and then
subsequently click again (with either clickpad or external mouse this
time) it registers a click at the location where my cursor icon was
originally. I can continue to move my cursor anywhere on the screen,
and it still registers the click in the original location.


cat /proc/version
Linux version 3.18.0-031800rc7-generic (apw@gomeisa) (gcc version
4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) ) #201411302035 SMP Mon Dec 1
01:36:38 UTC 2014


lsb_release -rd
Description: Ubuntu 14.10
Release: 14.10


sh /usr/src/linux-headers-3.18.0-031800rc7/scripts/ver_linux
If some fields are empty or look unusual you may have an old version.
Compare to the current minimal requirements in Documentation/Changes.

Linux woprjr 3.18.0-031800rc7-generic #201411302035 SMP Mon Dec 1
01:36:38 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

Gnu C
/usr/src/linux-headers-3.18.0-031800rc7/scripts/ver_linux:
/usr/src/linux-headers-3.18.0-031800rc7/scripts/ver_linux: 20:
/usr/src/linux-headers-3.18.0-031800rc7/scripts/ver_linux: ld: not
found
binutils
util-linux             2.25.1
mount                  debug
module-init-tools      18
e2fsprogs              1.42.10
PPP                    2.4.5
Linux C Library        2.19
Dynamic linker (ldd)   2.19
Procps                 3.3.9
Net-tools              1.60
Kbd                    1.15.5
Sh-utils               8.23
wireless-tools         30
Modules Loaded         snd_hda_codec_hdmi snd_hda_codec_realtek
snd_hda_codec_generic ctr ccm joydev hp_wmi hid_rmi sparse_keymap
rfcomm bnep arc4 rtl8723be btcoexist rtl8723_common intel_rapl
uvcvideo rtl_pci intel_soc_dts_thermal intel_powerclamp
videobuf2_vmalloc videobuf2_memops coretemp rtlwifi videobuf2_core
v4l2_common videodev mac80211 media kvm cfg80211 btusb
crct10dif_pclmul crc32_pclmul bluetooth ghash_clmulni_intel serio_raw
cryptd wmi snd_hda_intel snd_hda_controller snd_hda_codec snd_hwdep
snd_seq_midi snd_seq_midi_event snd_rawmidi snd_soc_rt5640
snd_soc_rl6231 snd_soc_core snd_compress dw_dmac i915
snd_pcm_dmaengine dw_dmac_core snd_seq int3400_thermal int3403_thermal
acpi_thermal_rel nls_iso8859_1 snd_pcm i2c_hid video
intel_smartconnect drm_kms_helper hid drm rfkill_gpio snd_seq_device
i2c_designware_platform i2c_algo_bit snd_soc_sst_acpi 8250_dw
snd_timer i2c_designware_core hp_wireless shpchp spi_pxa2xx_platform
snd pwm_lpss_platform lpc_ich mac_hid mei_txe pwm_lpss iosf_mbi mei
soundcore parport_pc ppdev lp parport uas usb_storage mmc_block
sdhci_acpi sdhci


cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 55
model name : Intel(R) Celeron(R) CPU  N2840  @ 2.16GHz
stepping : 8
microcode : 0x829
cpu MHz : 499.741
cache size : 1024 KB
physical id : 0
siblings : 2
core id : 0
cpu cores : 2
apicid : 0
initial apicid : 0
fpu : yes
fpu_exception : yes
cpuid level : 11
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov
pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx
rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology
nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2
ssse3 cx16 xtpr pdcm sse4_1 sse4_2 movbe popcnt tsc_deadline_timer
rdrand lahf_lm 3dnowprefetch ida arat epb dtherm tpr_shadow vnmi
flexpriority ept vpid tsc_adjust smep erms
bugs :
bogomips : 4326.40
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:

processor : 1
vendor_id : GenuineIntel
cpu family : 6
model : 55
model name : Intel(R) Celeron(R) CPU  N2840  @ 2.16GHz
stepping : 8
microcode : 0x829
cpu MHz : 499.741
cache size : 1024 KB
physical id : 0
siblings : 2
core id : 1
cpu cores : 2
apicid : 2
initial apicid : 2
fpu : yes
fpu_exception : yes
cpuid level : 11
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov
pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx
rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology
nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2
ssse3 cx16 xtpr pdcm sse4_1 sse4_2 movbe popcnt tsc_deadline_timer
rdrand lahf_lm 3dnowprefetch ida arat epb dtherm tpr_shadow vnmi
flexpriority ept vpid tsc_adjust smep erms
bugs :
bogomips : 4326.40
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:


cat /proc/modules
snd_hda_codec_hdmi 52670 1 - Live 0x0000000000000000
snd_hda_codec_realtek 80420 1 - Live 0x0000000000000000
snd_hda_codec_generic 69995 1 snd_hda_codec_realtek, Live 0x0000000000000000
ctr 13193 1 - Live 0x0000000000000000
ccm 17856 1 - Live 0x0000000000000000
joydev 17587 0 - Live 0x0000000000000000
hp_wmi 14017 0 - Live 0x0000000000000000
hid_rmi 18060 0 - Live 0x0000000000000000
sparse_keymap 13890 1 hp_wmi, Live 0x0000000000000000
rfcomm 75066 8 - Live 0x0000000000000000
bnep 23980 2 - Live 0x0000000000000000
arc4 12573 2 - Live 0x0000000000000000
rtl8723be 96097 0 - Live 0x0000000000000000
btcoexist 51822 1 rtl8723be, Live 0x0000000000000000
rtl8723_common 23662 1 rtl8723be, Live 0x0000000000000000
intel_rapl 19196 0 - Live 0x0000000000000000
uvcvideo 86723 0 - Live 0x0000000000000000
rtl_pci 27417 1 rtl8723be, Live 0x0000000000000000
intel_soc_dts_thermal 13523 0 - Live 0x0000000000000000
intel_powerclamp 19099 0 - Live 0x0000000000000000
videobuf2_vmalloc 13216 1 uvcvideo, Live 0x0000000000000000
videobuf2_memops 13362 1 videobuf2_vmalloc, Live 0x0000000000000000
coretemp 13638 0 - Live 0x0000000000000000
rtlwifi 74744 2 rtl8723be,rtl_pci, Live 0x0000000000000000
videobuf2_core 51547 1 uvcvideo, Live 0x0000000000000000
v4l2_common 15715 1 videobuf2_core, Live 0x0000000000000000
videodev 163831 3 uvcvideo,videobuf2_core,v4l2_common, Live 0x0000000000000000
mac80211 697159 3 rtl8723be,rtl_pci,rtlwifi, Live 0x0000000000000000
media 22008 2 uvcvideo,videodev, Live 0x0000000000000000
kvm 475237 0 - Live 0x0000000000000000
cfg80211 520257 2 rtlwifi,mac80211, Live 0x0000000000000000
btusb 32691 0 - Live 0x0000000000000000
crct10dif_pclmul 14268 0 - Live 0x0000000000000000
crc32_pclmul 13180 0 - Live 0x0000000000000000
bluetooth 486890 22 rfcomm,bnep,btusb, Live 0x0000000000000000
ghash_clmulni_intel 13230 0 - Live 0x0000000000000000
serio_raw 13483 0 - Live 0x0000000000000000
cryptd 20531 1 ghash_clmulni_intel, Live 0x0000000000000000
wmi 19379 1 hp_wmi, Live 0x0000000000000000
snd_hda_intel 30824 1 - Live 0x0000000000000000
snd_hda_controller 32234 1 snd_hda_intel, Live 0x0000000000000000
snd_hda_codec 144641 5
snd_hda_codec_hdmi,snd_hda_codec_realtek,snd_hda_codec_generic,snd_hda_intel,snd_hda_controller,
Live 0x0000000000000000
snd_hwdep 17709 1 snd_hda_codec, Live 0x0000000000000000
snd_seq_midi 13564 0 - Live 0x0000000000000000
snd_seq_midi_event 14899 1 snd_seq_midi, Live 0x0000000000000000
snd_rawmidi 31197 1 snd_seq_midi, Live 0x0000000000000000
snd_soc_rt5640 93325 0 - Live 0x0000000000000000
snd_soc_rl6231 13037 1 snd_soc_rt5640, Live 0x0000000000000000
snd_soc_core 207780 1 snd_soc_rt5640, Live 0x0000000000000000
snd_compress 19395 1 snd_soc_core, Live 0x0000000000000000
dw_dmac 12835 0 - Live 0x0000000000000000
i915 1031996 2 - Live 0x0000000000000000
snd_pcm_dmaengine 15229 1 snd_soc_core, Live 0x0000000000000000
dw_dmac_core 28558 1 dw_dmac, Live 0x0000000000000000
snd_seq 63540 2 snd_seq_midi,snd_seq_midi_event, Live 0x0000000000000000
int3400_thermal 13040 0 - Live 0x0000000000000000
int3403_thermal 13255 0 - Live 0x0000000000000000
acpi_thermal_rel 13807 1 int3400_thermal, Live 0x0000000000000000
nls_iso8859_1 12713 2 - Live 0x0000000000000000
snd_pcm 106273 7
snd_hda_codec_hdmi,snd_hda_intel,snd_hda_controller,snd_hda_codec,snd_soc_rt5640,snd_soc_core,snd_pcm_dmaengine,
Live 0x0000000000000000
i2c_hid 19065 0 - Live 0x0000000000000000
video 20649 1 i915, Live 0x0000000000000000
intel_smartconnect 12637 0 - Live 0x0000000000000000
drm_kms_helper 99802 1 i915, Live 0x0000000000000000
hid 110572 2 hid_rmi,i2c_hid, Live 0x0000000000000000
drm 323675 4 i915,drm_kms_helper, Live 0x0000000000000000
rfkill_gpio 12761 0 - Live 0x0000000000000000
snd_seq_device 14497 3 snd_seq_midi,snd_rawmidi,snd_seq, Live 0x0000000000000000
i2c_designware_platform 13025 0 - Live 0x0000000000000000
i2c_algo_bit 13564 1 i915, Live 0x0000000000000000
snd_soc_sst_acpi 13007 0 - Live 0x0000000000000000
8250_dw 13474 0 - Live 0x0000000000000000
snd_timer 30118 2 snd_seq,snd_pcm, Live 0x0000000000000000
i2c_designware_core 14990 1 i2c_designware_platform, Live 0x0000000000000000
hp_wireless 12637 0 - Live 0x0000000000000000
shpchp 37216 0 - Live 0x0000000000000000
spi_pxa2xx_platform 23256 0 - Live 0x0000000000000000
snd 84025 15 snd_hda_codec_hdmi,snd_hda_codec_realtek,snd_hda_codec_generic,snd_hda_intel,snd_hda_codec,snd_hwdep,snd_rawmidi,snd_soc_core,snd_compress,snd_seq,snd_pcm,snd_seq_device,snd_timer,
Live 0x0000000000000000
pwm_lpss_platform 12703 0 - Live 0x0000000000000000
lpc_ich 21176 0 - Live 0x0000000000000000
mac_hid 13275 0 - Live 0x0000000000000000
mei_txe 19808 0 - Live 0x0000000000000000
pwm_lpss 13281 1 pwm_lpss_platform, Live 0x0000000000000000
iosf_mbi 13865 1 intel_soc_dts_thermal, Live 0x0000000000000000
mei 88473 1 mei_txe, Live 0x0000000000000000
soundcore 15091 2 snd_hda_codec,snd, Live 0x0000000000000000
parport_pc 32909 0 - Live 0x0000000000000000
ppdev 17711 0 - Live 0x0000000000000000
lp 17799 0 - Live 0x0000000000000000
parport 42481 3 parport_pc,ppdev,lp, Live 0x0000000000000000
uas 22673 0 - Live 0x0000000000000000
usb_storage 67010 2 uas, Live 0x0000000000000000
mmc_block 36596 4 - Live 0x0000000000000000
sdhci_acpi 13502 0 - Live 0x0000000000000000
sdhci 43929 1 sdhci_acpi, Live 0x0000000000000000


/proc/ioports
0000-006f : PCI Bus 0000:00
  0000-001f : dma1
  0020-0021 : pic1
  0040-0043 : timer0
  0050-0053 : timer1
  0060-0060 : keyboard
  0062-0062 : PNP0C09:00
    0062-0062 : EC data
  0064-0064 : keyboard
  0066-0066 : PNP0C09:00
    0066-0066 : EC cmd
0070-0077 : rtc0
0078-0cf7 : PCI Bus 0000:00
  0080-008f : dma page reg
  00a0-00a1 : pic2
  00c0-00df : dma2
  00f0-00ff : fpu
  03f8-03ff : serial
  0400-047f : pnp 00:01
    0400-0403 : ACPI PM1a_EVT_BLK
    0404-0405 : ACPI PM1a_CNT_BLK
    0408-040b : ACPI PM_TMR
    0420-042f : ACPI GPE0_BLK
    0430-0433 : iTCO_wdt
    0450-0450 : ACPI PM2_CNT_BLK
    0460-047f : iTCO_wdt
  0500-05fe : pnp 00:01
  0600-061f : pnp 00:01
  0680-069f : pnp 00:01
0cf8-0cff : PCI conf1
0d00-ffff : PCI Bus 0000:00
  1000-1fff : PCI Bus 0000:01
    1000-10ff : 0000:01:00.0
      1000-10ff : rtl_pci
  2000-201f : 0000:00:1f.3
  2050-2057 : 0000:00:02.0


cat /proc/iomem
00000000-00000fff : reserved
00001000-0006efff : System RAM
0006f000-0006ffff : ACPI Non-volatile Storage
00070000-00084fff : System RAM
00085000-00086fff : reserved
00087000-00087fff : System RAM
00088000-0009ffff : reserved
000a0000-000bffff : PCI Bus 0000:00
000c0000-000dffff : PCI Bus 0000:00
000e0000-000fffff : PCI Bus 0000:00
  000f0000-000fffff : System ROM
00100000-1effffff : System RAM
  02000000-027b8537 : Kernel code
  027b8538-02d2123f : Kernel data
  02e87000-02fcffff : Kernel bss
1f000000-1f0fffff : reserved
1f100000-1fffffff : System RAM
20000000-200fffff : reserved
20100000-75867fff : System RAM
75868000-75892fff : reserved
75893000-78892fff : System RAM
78893000-78922fff : reserved
78923000-791a3fff : reserved
791a4000-792a2fff : ACPI Non-volatile Storage
792a3000-792e2fff : ACPI Tables
792e3000-79ffffff : System RAM
7a000000-7affffff : RAM buffer
7b000000-7effffff : reserved
  7b000000-7effffff : Graphics Stolen Memory
80000000-907ffffe : PCI Bus 0000:00
  80000000-8fffffff : 0000:00:02.0
  90000000-903fffff : 0000:00:02.0
  90400000-904fffff : PCI Bus 0000:01
    90400000-90403fff : 0000:01:00.0
      90400000-90403fff : rtl_pci
  90500000-905fffff : 0000:00:1a.0
    90500000-905fffff : mei_txe
  90600000-906fffff : 0000:00:1a.0
    90600000-906fffff : mei_txe
  90700000-9070ffff : 0000:00:14.0
    90700000-9070ffff : xhci-hcd
  90710000-90713fff : 0000:00:1b.0
    90710000-90713fff : ICH HD audio
  90718000-9071801f : 0000:00:1f.3
90800000-90800fff : 80860F14:01
  90800000-90800fff : 80860F14:01
90803000-90803fff : 80860F41:00
  90803000-90803fff : 80860F41:00
90804000-90807fff : INTL9C60:01
  90804000-90807fff : INTL9C60:01
90c00000-90ffffff : PCI Bus 0000:00
e0000000-e3ffffff : PCI MMCONFIG 0000 [bus 00-3f]
  e00000d0-e00000db : INT33BD:00
  e00f8000-e00f8fff : reserved
fec00000-fec003ff : IOAPIC 0
fed00000-fed003ff : HPET 0
  fed00000-fed003ff : PNP0103:00
fed01000-fed01fff : reserved
  fed01000-fed01fff : pnp 00:03
fed03000-fed03fff : pnp 00:03
  fed03008-fed0300c : iTCO_wdt
fed04000-fed04fff : pnp 00:03
fed05000-fed057ff : INT3401:00
fed08000-fed08fff : pnp 00:03
fed0c000-fed0cfff : INT33FC:00
  fed0c000-fed0cfff : INT33FC:00
fed0d000-fed0dfff : INT33FC:01
  fed0d000-fed0dfff : INT33FC:01
fed0e000-fed0efff : INT33FC:02
  fed0e000-fed0efff : INT33FC:02
fed1c000-fed1cfff : pnp 00:03
fed40000-fed44fff : pnp 00:01
fee00000-feefffff : pnp 00:03
  fee00000-fee00fff : Local APIC
fef00000-feffffff : pnp 00:03
ff000000-ffffffff : INT0800:00
  ffb80000-ffffffff : reserved


sudo lspci -vvv
00:00.0 Host bridge: Intel Corporation Atom Processor Z36xxx/Z37xxx
Series SoC Transaction Register (rev 0e)
Subsystem: Hewlett-Packard Company Device 8023
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx-
Status: Cap- 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort-
<TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0
Kernel driver in use: iosf_mbi_pci

00:02.0 VGA compatible controller: Intel Corporation Atom Processor
Z36xxx/Z37xxx Series Graphics & Display (rev 0e) (prog-if 00 [VGA
controller])
Subsystem: Hewlett-Packard Company Device 8023
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx+
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort-
<TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0
Interrupt: pin A routed to IRQ 263
Region 0: Memory at 90000000 (32-bit, non-prefetchable) [size=4M]
Region 2: Memory at 80000000 (32-bit, prefetchable) [size=256M]
Region 4: I/O ports at 2050 [size=8]
Expansion ROM at <unassigned> [disabled]
Capabilities: [d0] Power Management version 2
Flags: PMEClk- DSI+ D1- D2- AuxCurrent=0mA PME(D0-,D1-,D2-,D3hot-,D3cold-)
Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [90] MSI: Enable+ Count=1/1 Maskable- 64bit-
Address: fee0300c  Data: 4162
Capabilities: [b0] Vendor Specific Information: Len=07 <?>
Kernel driver in use: i915

00:14.0 USB controller: Intel Corporation Atom Processor Z36xxx/Z37xxx
Series USB xHCI (rev 0e) (prog-if 30 [XHCI])
Subsystem: Hewlett-Packard Company Device 8023
Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx+
Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort-
<TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0
Interrupt: pin A routed to IRQ 261
Region 0: Memory at 90700000 (64-bit, non-prefetchable) [size=64K]
Capabilities: [70] Power Management version 2
Flags: PMEClk- DSI- D1- D2- AuxCurrent=375mA PME(D0-,D1-,D2-,D3hot+,D3cold+)
Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [80] MSI: Enable+ Count=1/8 Maskable- 64bit+
Address: 00000000fee0300c  Data: 41d1
Kernel driver in use: xhci_hcd

00:1a.0 Encryption controller: Intel Corporation Atom Processor
Z36xxx/Z37xxx Series Trusted Execution Engine (rev 0e)
Subsystem: Hewlett-Packard Company Device 8023
Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR+ FastB2B- DisINTx+
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort-
<TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0, Cache Line Size: 64 bytes
Interrupt: pin A routed to IRQ 262
Region 0: Memory at 90600000 (32-bit, non-prefetchable) [size=1M]
Region 1: Memory at 90500000 (32-bit, non-prefetchable) [size=1M]
Capabilities: [80] Power Management version 3
Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0+,D1-,D2-,D3hot+,D3cold-)
Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [a0] MSI: Enable+ Count=1/1 Maskable- 64bit-
Address: fee0300c  Data: 4122
Kernel driver in use: mei_txe

00:1b.0 Audio device: Intel Corporation Atom Processor Z36xxx/Z37xxx
Series High Definition Audio Controller (rev 0e)
Subsystem: Hewlett-Packard Company Device 8023
Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx+
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort-
<TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0, Cache Line Size: 64 bytes
Interrupt: pin A routed to IRQ 264
Region 0: Memory at 90710000 (64-bit, non-prefetchable) [size=16K]
Capabilities: [50] Power Management version 2
Flags: PMEClk- DSI- D1- D2- AuxCurrent=55mA PME(D0+,D1-,D2-,D3hot+,D3cold+)
Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [60] MSI: Enable+ Count=1/1 Maskable- 64bit+
Address: 00000000fee0300c  Data: 4182
Kernel driver in use: snd_hda_intel

00:1c.0 PCI bridge: Intel Corporation Device 0f48 (rev 0e) (prog-if 00
[Normal decode])
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx-
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort-
<TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0, Cache Line Size: 64 bytes
Bus: primary=00, secondary=01, subordinate=01, sec-latency=0
I/O behind bridge: 00001000-00001fff
Memory behind bridge: 90400000-904fffff
Prefetchable memory behind bridge: 00000000fff00000-00000000000fffff
Secondary status: 66MHz- FastB2B- ParErr- DEVSEL=fast >TAbort-
<TAbort- <MAbort+ <SERR- <PERR-
BridgeCtl: Parity- SERR- NoISA- VGA- MAbort- >Reset- FastB2B-
PriDiscTmr- SecDiscTmr- DiscTmrStat- DiscTmrSERREn-
Capabilities: [40] Express (v2) Root Port (Slot+), MSI 00
DevCap: MaxPayload 128 bytes, PhantFunc 0
ExtTag- RBE+
DevCtl: Report errors: Correctable- Non-Fatal- Fatal- Unsupported-
RlxdOrd- ExtTag- PhantFunc- AuxPwr- NoSnoop-
MaxPayload 128 bytes, MaxReadReq 128 bytes
DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr+ TransPend-
LnkCap: Port #1, Speed 5GT/s, Width x1, ASPM L0s L1, Exit Latency L0s
<512ns, L1 <4us
ClockPM- Surprise- LLActRep+ BwNot+
LnkCtl: ASPM L0s L1 Enabled; RCB 64 bytes Disabled- CommClk+
ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
LnkSta: Speed 2.5GT/s, Width x1, TrErr- Train- SlotClk+ DLActive+
BWMgmt+ ABWMgmt-
SltCap: AttnBtn- PwrCtrl- MRL- AttnInd- PwrInd- HotPlug- Surprise-
Slot #0, PowerLimit 10.000W; Interlock- NoCompl+
SltCtl: Enable: AttnBtn- PwrFlt- MRL- PresDet- CmdCplt- HPIrq- LinkChg-
Control: AttnInd Unknown, PwrInd Unknown, Power- Interlock-
SltSta: Status: AttnBtn- PowerFlt- MRL- CmdCplt- PresDet+ Interlock-
Changed: MRL- PresDet- LinkState+
RootCtl: ErrCorrectable- ErrNon-Fatal- ErrFatal- PMEIntEna- CRSVisible-
RootCap: CRSVisible-
RootSta: PME ReqID 0000, PMEStatus- PMEPending-
DevCap2: Completion Timeout: Range BC, TimeoutDis+, LTR-, OBFF Not
Supported ARIFwd-
DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-, LTR-, OBFF
Disabled ARIFwd-
LnkCtl2: Target Link Speed: 5GT/s, EnterCompliance- SpeedDis-
 Transmit Margin: Normal Operating Range, EnterModifiedCompliance-
ComplianceSOS-
 Compliance De-emphasis: -6dB
LnkSta2: Current De-emphasis Level: -3.5dB, EqualizationComplete-,
EqualizationPhase1-
 EqualizationPhase2-, EqualizationPhase3-, LinkEqualizationRequest-
Capabilities: [80] MSI: Enable- Count=1/1 Maskable- 64bit-
Address: 00000000  Data: 0000
Capabilities: [90] Subsystem: Hewlett-Packard Company Device 8023
Capabilities: [a0] Power Management version 3
Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0+,D1-,D2-,D3hot+,D3cold+)
Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-
Kernel driver in use: pcieport

00:1f.0 ISA bridge: Intel Corporation Atom Processor Z36xxx/Z37xxx
Series Power Control Unit (rev 0e)
Subsystem: Hewlett-Packard Company Device 8023
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR+ FastB2B- DisINTx-
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=medium >TAbort-
<TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0
Capabilities: [e0] Vendor Specific Information: Len=0c <?>
Kernel driver in use: lpc_ich

00:1f.3 SMBus: Intel Corporation Device 0f12 (rev 0e)
Subsystem: Hewlett-Packard Company Device 8023
Control: I/O+ Mem+ BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx-
Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort-
<TAbort- <MAbort- >SERR- <PERR- INTx-
Interrupt: pin B routed to IRQ 255
Region 0: Memory at 90718000 (32-bit, non-prefetchable) [size=32]
Region 4: I/O ports at 2000 [size=32]
Capabilities: [50] Power Management version 3
Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0-,D1-,D2-,D3hot-,D3cold-)
Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME-

01:00.0 Network controller: Realtek Semiconductor Co., Ltd. RTL8723BE
PCIe Wireless Network Adapter
Subsystem: Hewlett-Packard Company Device 2231
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx-
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort-
<TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0, Cache Line Size: 64 bytes
Interrupt: pin A routed to IRQ 16
Region 0: I/O ports at 1000 [size=256]
Region 2: Memory at 90400000 (64-bit, non-prefetchable) [size=16K]
Capabilities: [40] Power Management version 3
Flags: PMEClk- DSI- D1+ D2+ AuxCurrent=375mA PME(D0+,D1+,D2+,D3hot+,D3cold+)
Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [50] MSI: Enable- Count=1/1 Maskable- 64bit+
Address: 0000000000000000  Data: 0000
Capabilities: [70] Express (v2) Endpoint, MSI 00
DevCap: MaxPayload 128 bytes, PhantFunc 0, Latency L0s <4us, L1 <64us
ExtTag- AttnBtn- AttnInd- PwrInd- RBE+ FLReset-
DevCtl: Report errors: Correctable- Non-Fatal- Fatal- Unsupported-
RlxdOrd+ ExtTag- PhantFunc- AuxPwr- NoSnoop-
MaxPayload 128 bytes, MaxReadReq 512 bytes
DevSta: CorrErr+ UncorrErr- FatalErr- UnsuppReq- AuxPwr+ TransPend-
LnkCap: Port #0, Speed 2.5GT/s, Width x1, ASPM L0s L1, Exit Latency
L0s <512ns, L1 <64us
ClockPM+ Surprise- LLActRep- BwNot-
LnkCtl: ASPM L0s L1 Enabled; RCB 64 bytes Disabled- CommClk+
ExtSynch- ClockPM+ AutWidDis- BWInt- AutBWInt-
LnkSta: Speed 2.5GT/s, Width x1, TrErr- Train- SlotClk+ DLActive-
BWMgmt- ABWMgmt-
DevCap2: Completion Timeout: Not Supported, TimeoutDis+, LTR+, OBFF
Via message/WAKE#
DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis+, LTR+, OBFF Disabled
LnkCtl2: Target Link Speed: 2.5GT/s, EnterCompliance- SpeedDis-
 Transmit Margin: Normal Operating Range, EnterModifiedCompliance-
ComplianceSOS-
 Compliance De-emphasis: -6dB
LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete-,
EqualizationPhase1-
 EqualizationPhase2-, EqualizationPhase3-, LinkEqualizationRequest-
Capabilities: [100 v2] Advanced Error Reporting
UESta: DLP- SDES- TLP- FCP+ CmpltTO- CmpltAbrt- UnxCmplt- RxOF-
MalfTLP- ECRC- UnsupReq- ACSViol-
UEMsk: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF-
MalfTLP- ECRC- UnsupReq- ACSViol-
UESvrt: DLP+ SDES+ TLP- FCP+ CmpltTO- CmpltAbrt- UnxCmplt- RxOF+
MalfTLP+ ECRC- UnsupReq- ACSViol-
CESta: RxErr+ BadTLP- BadDLLP+ Rollover- Timeout- NonFatalErr-
CEMsk: RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr+
AERCap: First Error Pointer: 0d, GenCap+ CGenEn- ChkCap+ ChkEn-
Capabilities: [140 v1] Device Serial Number 00-23-b7-fe-ff-4c-e0-00
Capabilities: [150 v1] Latency Tolerance Reporting
Max snoop latency: 71680ns
Max no snoop latency: 71680ns
Capabilities: [158 v1] L1 PM Substates
L1SubCap: PCI-PM_L1.2+ PCI-PM_L1.1+ ASPM_L1.2+ ASPM_L1.1+ L1_PM_Substates+
  PortCommonModeRestoreTime=150us PortTPowerOnTime=150us
Kernel driver in use: rtl8723be


cat /proc/scsi/scsi
Attached devices:
Host: scsi0 Channel: 00 Id: 00 Lun: 00
  Vendor: Generic- Model: SD/MMC/MS PRO    Rev: 1.00
  Type:   Direct-Access                    ANSI  SCSI revision: 04


ls /proc
1     12    14    1667  1745  18    1963  2345  292   35    470   55
7201  asound     execdomains  kmsg        pagetypeinfo   sysvipc
10    1218  1410  1685  1761  1804  2     2388  296   3573  471   554
728   buddyinfo  fb           kpagecount  partitions     thread-self
1020  125   142   1687  1764  1805  20    24    3     359   473   56
755   bus        filesystems  kpageflags  sched_debug    timer_list
1025  126   1420  1690  1767  1818  21    25    30    3623  474   57
76    cgroups    fs           loadavg     schedstat      timer_stats
1032  127   1423  17    1769  1828  2169  2528  31    378   485   585
77    cmdline    interrupts   locks       scsi           tty
1033  128   143   1701  1771  1853  2187  254   3181  3822  5     606
7886  consoles   iomem        mdstat      self           uptime
1038  129   1444  1710  1772  1876  2197  26    33    39    509   654
8     cpuinfo    ioports      meminfo     slabinfo       version
1042  13    1485  1718  1781  1883  2198  2670  3309  3932  516   655
8743  crypto     irq          misc        softirqs       vmallocinfo
1069  130   15    1722  1787  1893  22    2672  34    40    53    695
9     devices    kallsyms     modules     stat           vmstat
1070  131   1568  1725  1789  1896  2202  27    3419  41    539   7
9040  diskstats  kcore        mounts      swaps          zoneinfo
11    132   1645  1727  1792  19    2248  28    3433  468   542   709
9056  dma        keys         mtrr        sys
1144  1331  1655  1732  1796  1905  23    29    3437  469   5421  715
acpi  driver     key-users    net         sysrq-trigger

^ permalink raw reply

* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
From: jeffrey.lin @ 2014-12-08  3:42 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, shc_work, bleung, lee.jones,
	charliemooney
  Cc: jeffrey.lin, roger.yang, KP.li, linux-kernel, linux-input

From: "jeffrey.lin" <jeffrey.lin@rad-ic.com>

This patch is porting Raydium I2C touch driver. Developer can enable
 raydium touch driver by modifying define "CONFIG_TOUCHSCREEN_RM_TS".

Signed-off-by: jeffrey.lin@rad-ic.com
---
 drivers/input/touchscreen/Kconfig      |  12 +
 drivers/input/touchscreen/Makefile     |   1 +
 drivers/input/touchscreen/rm31100_ts.c | 846 +++++++++++++++++++++++++++++++++
 3 files changed, 859 insertions(+)
 create mode 100644 drivers/input/touchscreen/rm31100_ts.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3ce9181..d0324d2 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -955,4 +955,16 @@ config TOUCHSCREEN_ZFORCE
 	  To compile this driver as a module, choose M here: the
 	  module will be called zforce_ts.
 
+config TOUCHSCREEN_RM_TS
+	tristate "Raydium I2C Touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have Raydium series I2C touchscreen,
+	  such as RM31100 , connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rm31100_ts.
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 687d5a7..3220f66 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -78,3 +78,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+= zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)	+= tps6507x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)		+= rm31100_ts.o
diff --git a/drivers/input/touchscreen/rm31100_ts.c b/drivers/input/touchscreen/rm31100_ts.c
new file mode 100644
index 0000000..997dc0c
--- /dev/null
+++ b/drivers/input/touchscreen/rm31100_ts.c
@@ -0,0 +1,846 @@
+/*
+ * Raydium RM31100_ts touchscreen driver.
+ *
+ * Copyright (C) 2012-2014, Raydium Semiconductor Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Raydium reserves the right to make changes without further notice
+ * to the materials described herein. Raydium does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Raydium Semiconductor Corporation at www.rad-ic.com
+ *
+ * History:
+ *			(C) 2012 Raydium - Update for GPL distribution
+ *			(C) 2009 Enea - Original prototype
+ *
+ */
+#include <linux/async.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#ifdef CONFIG_MISC_DEV
+#include <linux/miscdevice.h>
+#endif
+#include <linux/uaccess.h>
+#include <asm/unaligned.h>
+#include <linux/input/mt.h>
+
+#define rm31100	0x0
+#define rm3110x	0x1
+
+#define INVALID_DATA	0xff
+#define MAX_REPORT_TOUCHED_POINTS	10
+
+#define I2C_CLIENT_ADDR         0x39
+#define I2C_DMA_CLIENT_ADDR     0x5A
+
+struct rm31100_ts_data {
+	u8 x_index;
+	u8 y_index;
+	u8 z_index;
+	u8 id_index;
+	u8 touch_index;
+	u8 data_reg;
+	u8 status_reg;
+	u8 data_size;
+	u8 touch_bytes;
+	u8 update_data;
+	u8 touch_meta_data;
+	u8 finger_size;
+};
+
+struct rm3110x_ts_platform_data {
+	int (*power_on)(int on);
+	int (*dev_setup)(bool on);
+	const char *ts_name;
+	u32 dis_min_x; /* display resoltion ABS min*/
+	u32 dis_max_x;/* display resoltion ABS max*/
+	u32 dis_min_y;
+	u32 dis_max_y;
+	u32 min_touch; /* no.of touches supported */
+	u32 max_touch;
+	u32 min_tid; /* track id */
+	u32 max_tid;
+	u32 min_width;/* size of the finger */
+	u32 max_width;
+	u32 res_x; /* TS resolution unit*/
+	u32 res_y;
+	u32 swap_xy;
+	u8 nfingers;
+	u32 irq_gpio;
+	int resout_gpio;
+	bool wakeup;
+	u32 irq_cfg;
+};
+
+static struct rm31100_ts_data devices[] = {
+	[0] = {
+		.x_index = 2,
+		.y_index = 4,
+		.z_index = 6,
+		.id_index = 1,
+		.data_reg = 0x1,
+		.status_reg = 0,
+		.update_data = 0x0,
+		.touch_bytes = 6,
+		.touch_meta_data = 1,
+		.finger_size = 70,
+	},
+};
+
+struct rm31100_ts {
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct rm3110x_ts_platform_data *pdata;
+	struct rm31100_ts_data *dd;
+	u8 *touch_data;
+	u8 device_id;
+	u8 prev_touches;
+	bool is_suspended;
+	bool int_pending;
+	struct mutex access_lock;
+	u32 pen_irq;
+};
+
+struct rm31100_ts *pts;
+/*
+static inline u16 join_bytes(u8 a, u8 b)
+{
+	u16 ab = 0;
+	ab = ab | a;
+	ab = ab << 8 | b;
+	return ab;
+}
+*/
+static s32 rm31100_ts_write_reg_u8(struct i2c_client *client, u8 reg, u8 val)
+{
+	s32 data;
+
+	data = i2c_smbus_write_byte_data(client, reg, val);
+	if (data < 0)
+		dev_err(&client->dev, "error %d in writing reg 0x%x\n",
+						 data, reg);
+
+	return data;
+}
+
+static s32 rm31100_ts_read_reg_u8(struct i2c_client *client, u8 reg)
+{
+	s32 data;
+
+	data = i2c_smbus_read_byte_data(client, reg);
+	if (data < 0)
+		dev_err(&client->dev, "error %d in reading reg 0x%x\n",
+						 data, reg);
+
+	return data;
+}
+
+static int rm31100_ts_read(struct i2c_client *client, u8 reg, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[2];
+
+	xfer_msg[0].addr = client->addr;
+	xfer_msg[0].len = 1;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = &reg;
+
+	xfer_msg[1].addr = client->addr;
+	xfer_msg[1].len = num;
+	xfer_msg[1].flags = I2C_M_RD;
+	xfer_msg[1].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, 2);
+}
+
+static int rm31100_ts_write_client_dma(struct i2c_client *client, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[1];
+
+	xfer_msg[0].addr = I2C_DMA_CLIENT_ADDR;
+	xfer_msg[0].len = num;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, 1);
+}
+
+static int rm31100_ts_write(struct i2c_client *client, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[1];
+
+	xfer_msg[0].addr = client->addr;
+	xfer_msg[0].len = num;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, 1);
+}
+#ifdef CONFIG_MISC_DEV
+static int dev_open(struct inode *inode, struct file *filp)
+{
+	mutex_lock(&pts->access_lock);
+	return 0;
+}
+
+static int dev_release(struct inode *inode, struct file *filp)
+{
+	mutex_unlock(&pts->access_lock);
+	return 0;
+}
+static ssize_t
+dev_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
+{
+	u8 *kbuf;
+	struct i2c_msg xfer_msg;
+	/*static char out[] = "1234567890";*/
+	/*static int idx;*//*= 0; remove by checkpatch*/
+	int i;
+
+	kbuf = kmalloc(count, GFP_KERNEL);
+	if (kbuf == NULL)
+		return -ENOMEM;
+
+	/*xfer_msg.addr = pts->client->addr;*/
+	xfer_msg.addr = I2C_CLIENT_ADDR;
+	xfer_msg.len = count;
+	xfer_msg.flags = I2C_M_RD;
+	xfer_msg.buf = kbuf;
+
+	i2c_transfer(pts->client->adapter, &xfer_msg, 1);
+
+	if (copy_to_user(buf, kbuf, count) == 0)
+		return count;
+	else
+		return -EFAULT;
+}
+
+static ssize_t
+dev_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
+{
+	u8 *kbuf;
+	ssize_t status = 0;
+	int i;
+
+	kbuf = kmalloc(count, GFP_KERNEL);
+	if (kbuf == NULL) {
+		dev_err("kmalloc() fail\n");
+		return -ENOMEM;
+	}
+
+	if (copy_from_user(kbuf, buf, count) == 0) {
+		pts->client->addr = I2C_CLIENT_ADDR;
+		if (rm31100_ts_write(pts->client, kbuf, count) < 0)
+			status = -EFAULT;
+		else
+			status = count;
+	} else {
+		dev_err("copy_from_user() fail\n");
+		status = -EFAULT;
+	}
+
+	kfree(kbuf);
+	return status;
+}
+
+static struct file_operations dev_fops = {
+	.owner = THIS_MODULE,
+	.open = dev_open,
+	.release = dev_release,
+	.read = dev_read,
+	.write = dev_write,
+	/*.unlocked_ioctl = dev_ioctl,*/
+};
+
+static struct miscdevice raydium_ts_miscdev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "raydium_ts",
+	.fops = &dev_fops,
+};
+#endif
+
+
+ssize_t show(struct device_driver *drv, char *buff)
+{
+	struct i2c_msg xfer_msg;
+	int num = 10;
+	char buf[100];
+	/*int i;*/
+
+	xfer_msg.addr = pts->client->addr;
+	xfer_msg.len = num;
+	xfer_msg.flags = I2C_M_RD;
+	xfer_msg.buf = buf;
+	pts->client->addr = I2C_CLIENT_ADDR;
+	i2c_transfer(pts->client->adapter, &xfer_msg, 1);
+
+	return 0;
+}
+
+ssize_t store(struct device_driver *drv, const char *buf, size_t count)
+{
+	/*unsigned char pkt[] = { 0xF2, 5, 1, 1 };*/
+	unsigned char pkt[] = { 0xF1, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
+
+	pts->client->addr = I2C_CLIENT_ADDR;
+	rm31100_ts_write(pts->client, pkt, sizeof(pkt));
+
+	return sizeof(pkt);
+}
+
+DRIVER_ATTR(myAttr, 0x777, show, store);
+
+static void report_data(struct rm31100_ts *dev, u16 x, u16 y,
+	u8 pressure, u8 id)
+{
+	struct input_dev *input_dev = dev->input;
+	if (dev->pdata->swap_xy)
+		swap(x, y);
+
+	input_mt_slot(input_dev, id);
+	input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
+	input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+	input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+	input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
+	input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, dev->dd->finger_size);
+/*
+	dev_dbg("%s(): id =%2hhd, x =%4hd, y =%4hd, pressure = %hhd\n",
+		__func__, id, x, y, pressure);
+*/
+}
+
+static void process_rm31100_data(struct rm31100_ts *ts)
+{
+	u8 id, pressure, touches, i;
+	u16 x, y;
+
+	touches = ts->touch_data[ts->dd->touch_index];
+
+	if (touches > 0) {
+		for (i = 0; i < touches; i++) {
+			id = ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->id_index];
+			pressure = ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->z_index];
+			x = get_unaligned_le16(&(ts->touch_data[i *
+				ts->dd->touch_bytes + ts->dd->x_index]));
+			y = get_unaligned_le16(&(ts->touch_data[i *
+				ts->dd->touch_bytes + ts->dd->y_index]));
+			report_data(ts, x, y, pressure, id);
+		}
+	} else
+		input_mt_sync(ts->input);
+
+	ts->prev_touches = touches;
+	/*input_report_key(ts->input, BTN_TOUCH, 1);*/
+	input_mt_report_pointer_emulation(ts->input, true);
+	input_sync(ts->input);
+}
+
+/*static void rm31100_ts_xy_worker(struct work_struct *work) JL remove*/
+static void rm31100_ts_xy_worker(struct rm31100_ts *work)
+{
+	int rc;
+	u8 client_dma_package[4] = {0x0f, 0x00, 0x20, 0x81};
+	struct rm31100_ts *ts = work;
+
+	if (ts->is_suspended == true) {
+		dev_dbg(&ts->client->dev, "TS is supended\n");
+		ts->int_pending = true;
+		return;
+	}
+
+	mutex_lock(&ts->access_lock);
+	/* read data from DATA_REG */
+	/*RM31100 DMA Mode*/
+	rc = rm31100_ts_write_client_dma(ts->client, client_dma_package, 0x04);
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "write client dma failed\n");
+		goto schedule;
+	}
+	rc = rm31100_ts_read(ts->client, ts->dd->data_reg, ts->touch_data,
+	ts->dd->data_size);
+
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "read failed\n");
+		goto schedule;
+	}
+
+	if (ts->touch_data[ts->dd->touch_index] == INVALID_DATA)
+		goto schedule;
+
+	/* write to STATUS_REG to release lock */
+	rc = rm31100_ts_write_reg_u8(ts->client,
+		ts->dd->status_reg, ts->dd->update_data);
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "write failed, try once more\n");
+
+		rc = rm31100_ts_write_reg_u8(ts->client,
+			ts->dd->status_reg, ts->dd->update_data);
+		if (rc < 0)
+			dev_err(&ts->client->dev, "write failed, exiting\n");
+	}
+
+	process_rm31100_data(ts);
+schedule:
+	mutex_unlock(&ts->access_lock);
+}
+
+static irqreturn_t rm31100_ts_irq(int irq, void *dev_id)
+{
+	struct rm31100_ts *ts = dev_id;
+
+	rm31100_ts_xy_worker(ts);
+	return IRQ_HANDLED;
+}
+
+static int rm31100_ts_init_ts(struct i2c_client *client, struct rm31100_ts *ts)
+{
+	/*struct input_dev *input_device;*/
+	/*int rc = 0;*/
+
+	ts->dd = &devices[ts->device_id];
+
+	if (!ts->pdata->nfingers) {
+		dev_err(&client->dev, "Touches information not specified\n");
+		return -EINVAL;
+	}
+
+	if (ts->device_id == rm3110x) {
+		if (ts->pdata->nfingers > 2) {
+			dev_err(&client->dev, "Touches >=1 & <= 2\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->dd->touch_bytes;
+		ts->dd->touch_index = 0x0;
+	} else if (ts->device_id == rm31100) {
+		if (ts->pdata->nfingers > 10) {
+			dev_err(&client->dev, "Touches >=1 & <= 10\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+						ts->dd->touch_meta_data;
+		ts->dd->touch_index = 0x0;
+	}
+	/* w001 */
+	else {
+		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+		ts->dd->touch_meta_data;
+		ts->dd->touch_index = 0x0;
+	}
+
+	ts->touch_data = kzalloc(ts->dd->data_size, GFP_KERNEL);
+	if (!ts->touch_data) {
+		pr_err("%s: Unable to allocate memory\n", __func__);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int rm31100_ts_suspend(struct device *dev)
+{
+	struct rm31100_ts *ts = dev_get_drvdata(dev);
+	int rc = 0;
+
+	if (device_may_wakeup(dev)) {
+		/* mark suspend flag */
+		ts->is_suspended = true;
+		enable_irq_wake(ts->pen_irq);
+	}
+
+	disable_irq(ts->pen_irq);
+
+	gpio_free(ts->pdata->irq_gpio);
+
+	if (ts->pdata->power_on) {
+		rc = ts->pdata->power_on(0);
+		if (rc) {
+			dev_err(dev, "unable to goto suspend\n");
+			return rc;
+		}
+	}
+	return 0;
+}
+
+static int rm31100_ts_resume(struct device *dev)
+{
+	struct rm31100_ts *ts = dev_get_drvdata(dev);
+
+	int rc = 0;
+
+	if (device_may_wakeup(dev)) {
+		disable_irq_wake(ts->pen_irq);
+
+		ts->is_suspended = false;
+
+		if (ts->int_pending == true)
+			ts->int_pending = false;
+	} else {
+		if (ts->pdata->power_on) {
+			rc = ts->pdata->power_on(1);
+			if (rc) {
+				dev_err(dev, "unable to resume\n");
+				return rc;
+			}
+		}
+
+		enable_irq(ts->pen_irq);
+
+		/* Clear the status register of the TS controller */
+		rc = rm31100_ts_write_reg_u8(ts->client,
+			ts->dd->status_reg, ts->dd->update_data);
+		if (rc < 0) {
+			dev_err(&ts->client->dev,
+				"write failed, try once more\n");
+
+			rc = rm31100_ts_write_reg_u8(ts->client,
+				ts->dd->status_reg,
+				ts->dd->update_data);
+			if (rc < 0)
+				dev_err(&ts->client->dev,
+					"write failed, exiting\n");
+		}
+	}
+
+	return 0;
+}
+
+static struct dev_pm_ops rm31100_ts_pm_ops = {
+	.suspend = rm31100_ts_suspend,
+	.resume = rm31100_ts_resume,
+};
+#endif
+
+static int rm_input_dev_create(struct rm31100_ts *ts)
+{
+	struct input_dev *input_device;
+	int rc = 0;
+	ts->prev_touches = 0;
+
+	input_device = input_allocate_device();
+	if (!input_device) {
+		rc = -ENOMEM;
+		goto error_alloc_dev;
+	}
+	ts->input = input_device;
+
+	input_device->name = "raydium_ts";
+	input_device->id.bustype = BUS_I2C;
+	input_device->dev.parent = &ts->client->dev;
+	input_set_drvdata(input_device, ts);
+
+	__set_bit(EV_ABS, input_device->evbit);
+	__set_bit(INPUT_PROP_DIRECT, input_device->propbit);
+	__set_bit(BTN_TOUCH, input_device->keybit);
+
+
+	if (ts->device_id == rm31100) {
+		/* set up virtual key */
+		__set_bit(EV_KEY, input_device->evbit);
+	}
+	input_mt_init_slots(input_device,
+		MAX_REPORT_TOUCHED_POINTS, 0);
+	input_set_abs_params(input_device, ABS_MT_POSITION_X,
+		ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_POSITION_Y,
+		ts->pdata->dis_min_y, ts->pdata->dis_max_y, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_PRESSURE,
+		0, 0xFF, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_TOUCH_MAJOR,
+		0, 0xFF, 0, 0);
+	rc = input_register_device(input_device);
+	if (rc)
+		goto error_unreg_device;
+
+	return 0;
+
+error_unreg_device:
+	input_free_device(input_device);
+error_alloc_dev:
+	ts->input = NULL;
+	return rc;
+}
+
+static int rm31100_initialize(struct i2c_client *client)
+{
+	struct rm31100_ts *ts = i2c_get_clientdata(client);
+	int rc = 0, /*retry_cnt = 0,*/ temp_reg;
+	/* power on the device */
+	if (ts->pdata->power_on) {
+		rc = ts->pdata->power_on(1);
+		if (rc) {
+			pr_err("%s: Unable to power on the device\n", __func__);
+			goto error_dev_setup;
+		}
+	}
+
+	/* read one byte to make sure i2c device exists */
+	if (ts->device_id == rm3110x)
+		temp_reg = 0x01;
+	else if (ts->device_id == rm31100)
+		temp_reg = 0x00;
+	else
+		temp_reg = 0x05;
+
+	rc = rm31100_ts_read_reg_u8(client, temp_reg);
+	if (rc < 0) {
+		dev_err(&client->dev, "i2c sanity check failed\n");
+		goto error_power_on;
+	}
+
+	rc = rm31100_ts_init_ts(client, ts);
+	if (rc < 0) {
+		dev_err(&client->dev, "rm31100_ts init failed\n");
+		goto error_mutex_destroy;
+	}
+
+	/* configure touchscreen reset out gpio */
+	rc = gpio_request(ts->pdata->resout_gpio, "rm31100_resout_gpio");
+	if (rc) {
+		pr_err("%s: unable to request gpio %d\n",
+			__func__, ts->pdata->resout_gpio);
+		goto error_uninit_ts;
+	}
+
+	rc = gpio_direction_output(ts->pdata->resout_gpio, 0);
+	if (rc) {
+		pr_err("%s: unable to set direction for gpio %d\n",
+			__func__, ts->pdata->resout_gpio);
+		goto error_resout_gpio_dir;
+	}
+	/* reset gpio stabilization time */
+	msleep(20);
+
+	return 0;
+error_resout_gpio_dir:
+	if (ts->pdata->resout_gpio >= 0)
+		gpio_free(ts->pdata->resout_gpio);
+error_uninit_ts:
+	input_unregister_device(ts->input);
+	kfree(ts->touch_data);
+error_mutex_destroy:
+	mutex_destroy(&ts->access_lock);
+error_power_on:
+	if (ts->pdata->power_on)
+		ts->pdata->power_on(0);
+error_dev_setup:
+	if (ts->pdata->dev_setup)
+		ts->pdata->dev_setup(0);
+	return rc;
+}
+
+static void rm_initialize_async(void *data, async_cookie_t cookie)
+{
+	struct rm31100_ts *ts = data;
+	struct i2c_client *client = ts->client;
+	unsigned long irqflags;
+	int err = 0;
+
+	mutex_lock(&ts->access_lock);
+
+	err = rm31100_initialize(client);
+	if (err < 0) {
+		dev_err(&client->dev, "probe failed! unbind device.\n");
+		goto error_free_mem;
+	}
+
+	err = rm_input_dev_create(ts);
+	if (err) {
+		dev_err(&client->dev, "%s crated failed, %d\n", __func__, err);
+		goto error_goio_release;
+	}
+
+	irqflags = client->dev.of_node ? 0 : IRQF_TRIGGER_FALLING;
+
+	err = request_threaded_irq(ts->pen_irq, NULL,
+				   rm31100_ts_irq,
+				   irqflags | IRQF_ONESHOT,
+				   ts->client->dev.driver->name, ts);
+	if (err) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		goto error_dev_release;
+	}
+
+	mutex_unlock(&ts->access_lock);
+
+	return;
+
+error_dev_release:
+	input_free_device(ts->input);
+error_goio_release:
+	if (ts->pdata->resout_gpio >= 0)
+		gpio_free(ts->pdata->resout_gpio);
+error_free_mem:
+	mutex_unlock(&ts->access_lock);
+	kfree(ts);
+	return;
+}
+
+
+/*static int __devinit rm31100_ts_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)*/
+static int rm31100_ts_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct rm31100_ts *ts;
+	struct rm3110x_ts_platform_data *pdata = client->dev.platform_data;
+	int rc/*, temp_reg*/;
+	union i2c_smbus_data dummy;
+
+	if (!pdata) {
+		dev_err(&client->dev, "platform data is required!\n");
+		return -EINVAL;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+		dev_err(&client->dev, "I2C functionality not supported\n");
+		return -EIO;
+	}
+	/* Make sure there is something at this address */
+	if (i2c_smbus_xfer(client->adapter, client->addr, 0,
+		I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0)
+		return -ENODEV;
+
+	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+	pts = ts;
+
+	/* Enable runtime PM ops, start in ACTIVE mode */
+	rc = pm_runtime_set_active(&client->dev);
+	if (rc < 0)
+		dev_warn(&client->dev, "unable to set runtime pm state\n");
+	pm_runtime_enable(&client->dev);
+
+	ts->client = client;
+	ts->pdata = pdata;
+	i2c_set_clientdata(client, ts);
+	ts->device_id = id->driver_data;
+
+	if (ts->pdata->dev_setup) {
+		rc = ts->pdata->dev_setup(1);
+		if (rc < 0) {
+			dev_err(&client->dev, "dev setup failed\n");
+			goto error_touch_data_alloc;
+		}
+	}
+
+
+	ts->is_suspended = false;
+	ts->int_pending = false;
+	/*mutex_init(&ts->sus_lock); JL remove*/
+	mutex_init(&ts->access_lock);
+
+	async_schedule(rm_initialize_async, ts);
+
+	device_init_wakeup(&client->dev, ts->pdata->wakeup);
+	return 0;
+
+error_touch_data_alloc:
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_disable(&client->dev);
+	kfree(ts);
+	return rc;
+}
+
+/*static int __devexit RM31100_ts_remove(struct i2c_client *client)*/
+static int rm31100_ts_remove(struct i2c_client *client)
+{
+	struct rm31100_ts *ts = i2c_get_clientdata(client);
+
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_disable(&client->dev);
+
+	device_init_wakeup(&client->dev, 0);
+	free_irq(ts->pen_irq, ts);
+
+	gpio_free(ts->pdata->irq_gpio);
+
+	if (ts->pdata->resout_gpio >= 0)
+		gpio_free(ts->pdata->resout_gpio);
+	input_unregister_device(ts->input);
+
+	/*mutex_destroy(&ts->sus_lock); JL remove*/
+	mutex_destroy(&ts->access_lock);
+
+	if (ts->pdata->power_on)
+		ts->pdata->power_on(0);
+
+	if (ts->pdata->dev_setup)
+		ts->pdata->dev_setup(0);
+
+	kfree(ts->touch_data);
+	kfree(ts);
+
+	return 0;
+}
+
+static const struct i2c_device_id rm31100_ts_id[] = {
+	{"RM31100", rm31100},
+	{"RM3110x", rm3110x},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, rm31100_ts_id);
+
+
+static struct i2c_driver rm31100_ts_driver = {
+	.driver = {
+		.name = "raydium_ts",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &rm31100_ts_pm_ops,
+#endif
+	},
+	.probe = rm31100_ts_probe,
+	.remove = rm31100_ts_remove,
+	.id_table = rm31100_ts_id,
+};
+
+static int __init rm31100_ts_init(void)
+{
+	int rc;
+	int rc2;
+
+	rc = i2c_add_driver(&rm31100_ts_driver);
+
+	rc2 = driver_create_file(&rm31100_ts_driver.driver,
+		&driver_attr_myAttr);
+
+	return rc;
+}
+/* Making this as late init to avoid power fluctuations
+ * during LCD initialization.
+ */
+module_init(rm31100_ts_init);
+
+static void __exit rm31100_ts_exit(void)
+{
+	i2c_del_driver(&rm31100_ts_driver);
+}
+module_exit(rm31100_ts_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("rm31100-rm3110x touchscreen controller driver");
+MODULE_AUTHOR("Raydium");
+MODULE_ALIAS("platform:rm31100_ts");
-- 
2.1.2


^ permalink raw reply related


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