Linux Input/HID development
 help / color / mirror / Atom feed
* Regression in kernel 3.13: mouse freezes after entering in KDE (doesn't happen in 3.12)
From: Dâniel Fraga @ 2014-03-04 17:54 UTC (permalink / raw)
  To: linux-input

	After I upgraded my kernel from 3.12 to 3.13, the mouse gets
stuck (freezes) after entering in KDE. The interesting is that the
mouse works with gpm at the console and in KDM (before login). Just
after I login into KDE, it freezes (and it freezes when Bitcoin-qt is
starting which is very disk intensive at the start... maybe something
related to high load?).

	My hardware:

Asus P8Z68-V Pro/Gen3
Core i7 2700k
HD Seagate 1.5 TB (using ext4)

	I tried to bisect between 3.12 and 3.13 but it failed to find
the bad commit. For example:

1) I bisect between 3.12 and 3.13:

37676af15c8d5a9689c9d1220d2a27d510cbe238 is the first bad commit

2) I bisect between 3.12 and 37676af15c8d5a9689c9d1220d2a27d510cbe238

afc4b47372ace24c630c1d79b3a0ed32bf1d19fd is the first bad commit

	And this goes on forever. The strangest fact is that bisect
test against commits which it shouldn't test like commits before 3.12
release (3.12-rc7 etc).

	So I'm asking for suggestions for what can I try to better find
the bad commit. 3.13-rc1 also fails, so the bad commit is between 3.12
and 3.13-rc1. I tried to bisect between 3.12 and 3.13-rc1 but without
success, since it points to a "bad commit" after the real bad commit (I
know because I always test with the previous commit before the "bad
commit" and it fails too).

	Any thins what I can do?

	Thanks.

	Ps: better yet, could you suggest commits I could test, so I
can report back and we can do a more rational bisect?

-- 
Linux 3.12.13: One Giant Leap for Frogkind
http://www.youtube.com/DanielFragaBR
http://mcxnow.com?r=danielfragabr
Bitcoin: 12H6661yoLDUZaYPdah6urZS5WiXwTAUgL



^ permalink raw reply

* [PATCH v5 1/9] Input: pmic8xxx-keypad - Fix build by removing gpio configuration
From: Stephen Boyd @ 2014-03-04 18:34 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-arm-msm, Josh Cartwright, linux-kernel, linux-arm-kernel,
	linux-input
In-Reply-To: <1393958088-1456-1-git-send-email-sboyd@codeaurora.org>

The gpio configuration in this driver doesn't work because the
gpio.h include doesn't exist. Remove the configuration as it
isn't strictly necessary, allowing us to actually compile this
driver. If it's needed in the future, it should be done via a
pinctrl driver.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 drivers/input/keyboard/pmic8xxx-keypad.c | 58 --------------------------------
 1 file changed, 58 deletions(-)

diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c
index 2c9f19ac35ea..c4aa882c8fb9 100644
--- a/drivers/input/keyboard/pmic8xxx-keypad.c
+++ b/drivers/input/keyboard/pmic8xxx-keypad.c
@@ -21,7 +21,6 @@
 #include <linux/mutex.h>
 
 #include <linux/mfd/pm8xxx/core.h>
-#include <linux/mfd/pm8xxx/gpio.h>
 #include <linux/input/pmic8xxx-keypad.h>
 
 #define PM8XXX_MAX_ROWS		18
@@ -447,27 +446,6 @@ static int pmic8xxx_kpd_init(struct pmic8xxx_kp *kp)
 
 }
 
-static int  pmic8xxx_kp_config_gpio(int gpio_start, int num_gpios,
-			struct pmic8xxx_kp *kp, struct pm_gpio *gpio_config)
-{
-	int	rc, i;
-
-	if (gpio_start < 0 || num_gpios < 0)
-		return -EINVAL;
-
-	for (i = 0; i < num_gpios; i++) {
-		rc = pm8xxx_gpio_config(gpio_start + i, gpio_config);
-		if (rc) {
-			dev_err(kp->dev, "%s: FAIL pm8xxx_gpio_config():"
-					"for PM GPIO [%d] rc=%d.\n",
-					__func__, gpio_start + i, rc);
-			return rc;
-		}
-	 }
-
-	return 0;
-}
-
 static int pmic8xxx_kp_enable(struct pmic8xxx_kp *kp)
 {
 	int rc;
@@ -527,27 +505,6 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
 	int rc;
 	u8 ctrl_val;
 
-	struct pm_gpio kypd_drv = {
-		.direction	= PM_GPIO_DIR_OUT,
-		.output_buffer	= PM_GPIO_OUT_BUF_OPEN_DRAIN,
-		.output_value	= 0,
-		.pull		= PM_GPIO_PULL_NO,
-		.vin_sel	= PM_GPIO_VIN_S3,
-		.out_strength	= PM_GPIO_STRENGTH_LOW,
-		.function	= PM_GPIO_FUNC_1,
-		.inv_int_pol	= 1,
-	};
-
-	struct pm_gpio kypd_sns = {
-		.direction	= PM_GPIO_DIR_IN,
-		.pull		= PM_GPIO_PULL_UP_31P5,
-		.vin_sel	= PM_GPIO_VIN_S3,
-		.out_strength	= PM_GPIO_STRENGTH_NO,
-		.function	= PM_GPIO_FUNC_NORMAL,
-		.inv_int_pol	= 1,
-	};
-
-
 	if (!pdata || !pdata->num_cols || !pdata->num_rows ||
 		pdata->num_cols > PM8XXX_MAX_COLS ||
 		pdata->num_rows > PM8XXX_MAX_ROWS ||
@@ -653,20 +610,6 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
 		goto err_get_irq;
 	}
 
-	rc = pmic8xxx_kp_config_gpio(pdata->cols_gpio_start,
-					pdata->num_cols, kp, &kypd_sns);
-	if (rc < 0) {
-		dev_err(&pdev->dev, "unable to configure keypad sense lines\n");
-		goto err_gpio_config;
-	}
-
-	rc = pmic8xxx_kp_config_gpio(pdata->rows_gpio_start,
-					pdata->num_rows, kp, &kypd_drv);
-	if (rc < 0) {
-		dev_err(&pdev->dev, "unable to configure keypad drive lines\n");
-		goto err_gpio_config;
-	}
-
 	rc = request_any_context_irq(kp->key_sense_irq, pmic8xxx_kp_irq,
 				 IRQF_TRIGGER_RISING, "pmic-keypad", kp);
 	if (rc < 0) {
@@ -703,7 +646,6 @@ err_pmic_reg_read:
 	free_irq(kp->key_stuck_irq, kp);
 err_req_stuck_irq:
 	free_irq(kp->key_sense_irq, kp);
-err_gpio_config:
 err_get_irq:
 	input_free_device(kp->input);
 err_alloc_device:
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply related

* [PATCH v5 2/9] Input: pmic8xxx-keypad - Migrate to devm_* APIs
From: Stephen Boyd @ 2014-03-04 18:34 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-arm-msm, Josh Cartwright, linux-kernel, linux-arm-kernel,
	linux-input
In-Reply-To: <1393958088-1456-1-git-send-email-sboyd@codeaurora.org>

Simplify the error paths and reduce the lines of code in this
driver by using the devm_* APIs.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 drivers/input/keyboard/pmic8xxx-keypad.c | 61 +++++++++-----------------------
 1 file changed, 17 insertions(+), 44 deletions(-)

diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c
index c4aa882c8fb9..bec53ebde7b2 100644
--- a/drivers/input/keyboard/pmic8xxx-keypad.c
+++ b/drivers/input/keyboard/pmic8xxx-keypad.c
@@ -543,7 +543,7 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
 		return -EINVAL;
 	}
 
-	kp = kzalloc(sizeof(*kp), GFP_KERNEL);
+	kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL);
 	if (!kp)
 		return -ENOMEM;
 
@@ -552,32 +552,27 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
 	kp->pdata	= pdata;
 	kp->dev		= &pdev->dev;
 
-	kp->input = input_allocate_device();
+	kp->input = devm_input_allocate_device(&pdev->dev);
 	if (!kp->input) {
 		dev_err(&pdev->dev, "unable to allocate input device\n");
-		rc = -ENOMEM;
-		goto err_alloc_device;
+		return -ENOMEM;
 	}
 
 	kp->key_sense_irq = platform_get_irq(pdev, 0);
 	if (kp->key_sense_irq < 0) {
 		dev_err(&pdev->dev, "unable to get keypad sense irq\n");
-		rc = -ENXIO;
-		goto err_get_irq;
+		return kp->key_sense_irq;
 	}
 
 	kp->key_stuck_irq = platform_get_irq(pdev, 1);
 	if (kp->key_stuck_irq < 0) {
 		dev_err(&pdev->dev, "unable to get keypad stuck irq\n");
-		rc = -ENXIO;
-		goto err_get_irq;
+		return kp->key_stuck_irq;
 	}
 
 	kp->input->name = pdata->input_name ? : "PMIC8XXX keypad";
 	kp->input->phys = pdata->input_phys_device ? : "pmic8xxx_keypad/input0";
 
-	kp->input->dev.parent	= &pdev->dev;
-
 	kp->input->id.bustype	= BUS_I2C;
 	kp->input->id.version	= 0x0001;
 	kp->input->id.product	= 0x0001;
@@ -591,7 +586,7 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
 					kp->keycodes, kp->input);
 	if (rc) {
 		dev_err(&pdev->dev, "failed to build keymap\n");
-		goto err_get_irq;
+		return rc;
 	}
 
 	if (pdata->rep)
@@ -607,27 +602,29 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
 	rc = pmic8xxx_kpd_init(kp);
 	if (rc < 0) {
 		dev_err(&pdev->dev, "unable to initialize keypad controller\n");
-		goto err_get_irq;
+		return rc;
 	}
 
-	rc = request_any_context_irq(kp->key_sense_irq, pmic8xxx_kp_irq,
-				 IRQF_TRIGGER_RISING, "pmic-keypad", kp);
+	rc = devm_request_any_context_irq(&pdev->dev, kp->key_sense_irq,
+			pmic8xxx_kp_irq, IRQF_TRIGGER_RISING, "pmic-keypad",
+			kp);
 	if (rc < 0) {
 		dev_err(&pdev->dev, "failed to request keypad sense irq\n");
-		goto err_get_irq;
+		return rc;
 	}
 
-	rc = request_any_context_irq(kp->key_stuck_irq, pmic8xxx_kp_stuck_irq,
-				 IRQF_TRIGGER_RISING, "pmic-keypad-stuck", kp);
+	rc = devm_request_any_context_irq(&pdev->dev, kp->key_stuck_irq,
+			pmic8xxx_kp_stuck_irq, IRQF_TRIGGER_RISING,
+			"pmic-keypad-stuck", kp);
 	if (rc < 0) {
 		dev_err(&pdev->dev, "failed to request keypad stuck irq\n");
-		goto err_req_stuck_irq;
+		return rc;
 	}
 
 	rc = pmic8xxx_kp_read_u8(kp, &ctrl_val, KEYP_CTRL);
 	if (rc < 0) {
 		dev_err(&pdev->dev, "failed to read KEYP_CTRL register\n");
-		goto err_pmic_reg_read;
+		return rc;
 	}
 
 	kp->ctrl_reg = ctrl_val;
@@ -635,35 +632,12 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
 	rc = input_register_device(kp->input);
 	if (rc < 0) {
 		dev_err(&pdev->dev, "unable to register keypad input device\n");
-		goto err_pmic_reg_read;
+		return rc;
 	}
 
 	device_init_wakeup(&pdev->dev, pdata->wakeup);
 
 	return 0;
-
-err_pmic_reg_read:
-	free_irq(kp->key_stuck_irq, kp);
-err_req_stuck_irq:
-	free_irq(kp->key_sense_irq, kp);
-err_get_irq:
-	input_free_device(kp->input);
-err_alloc_device:
-	kfree(kp);
-	return rc;
-}
-
-static int pmic8xxx_kp_remove(struct platform_device *pdev)
-{
-	struct pmic8xxx_kp *kp = platform_get_drvdata(pdev);
-
-	device_init_wakeup(&pdev->dev, 0);
-	free_irq(kp->key_stuck_irq, kp);
-	free_irq(kp->key_sense_irq, kp);
-	input_unregister_device(kp->input);
-	kfree(kp);
-
-	return 0;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -713,7 +687,6 @@ static SIMPLE_DEV_PM_OPS(pm8xxx_kp_pm_ops,
 
 static struct platform_driver pmic8xxx_kp_driver = {
 	.probe		= pmic8xxx_kp_probe,
-	.remove		= pmic8xxx_kp_remove,
 	.driver		= {
 		.name = PM8XXX_KEYPAD_DEV_NAME,
 		.owner = THIS_MODULE,
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply related

* [PATCH v5 3/9] Input: pmic8xxx-keypad - Migrate to regmap APIs
From: Stephen Boyd @ 2014-03-04 18:34 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-arm-msm, Josh Cartwright, linux-kernel, linux-arm-kernel,
	linux-input
In-Reply-To: <1393958088-1456-1-git-send-email-sboyd@codeaurora.org>

Use the regmap APIs for this driver instead of custom pm8xxx
APIs. This breaks this driver's dependency on the pm8xxx APIs and
allows us to easily port it to other bus protocols in the future.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 drivers/input/keyboard/pmic8xxx-keypad.c | 81 ++++++++++++--------------------
 1 file changed, 29 insertions(+), 52 deletions(-)

diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c
index bec53ebde7b2..0efd11e16b7e 100644
--- a/drivers/input/keyboard/pmic8xxx-keypad.c
+++ b/drivers/input/keyboard/pmic8xxx-keypad.c
@@ -19,8 +19,8 @@
 #include <linux/bitops.h>
 #include <linux/delay.h>
 #include <linux/mutex.h>
+#include <linux/regmap.h>
 
-#include <linux/mfd/pm8xxx/core.h>
 #include <linux/input/pmic8xxx-keypad.h>
 
 #define PM8XXX_MAX_ROWS		18
@@ -86,6 +86,7 @@
  * struct pmic8xxx_kp - internal keypad data structure
  * @pdata - keypad platform data pointer
  * @input - input device pointer for keypad
+ * @regmap - regmap handle
  * @key_sense_irq - key press/release irq number
  * @key_stuck_irq - key stuck notification irq number
  * @keycodes - array to hold the key codes
@@ -97,6 +98,7 @@
 struct pmic8xxx_kp {
 	const struct pm8xxx_keypad_platform_data *pdata;
 	struct input_dev *input;
+	struct regmap *regmap;
 	int key_sense_irq;
 	int key_stuck_irq;
 
@@ -109,33 +111,6 @@ struct pmic8xxx_kp {
 	u8 ctrl_reg;
 };
 
-static int pmic8xxx_kp_write_u8(struct pmic8xxx_kp *kp,
-				 u8 data, u16 reg)
-{
-	int rc;
-
-	rc = pm8xxx_writeb(kp->dev->parent, reg, data);
-	return rc;
-}
-
-static int pmic8xxx_kp_read(struct pmic8xxx_kp *kp,
-				 u8 *data, u16 reg, unsigned num_bytes)
-{
-	int rc;
-
-	rc = pm8xxx_read_buf(kp->dev->parent, reg, data, num_bytes);
-	return rc;
-}
-
-static int pmic8xxx_kp_read_u8(struct pmic8xxx_kp *kp,
-				 u8 *data, u16 reg)
-{
-	int rc;
-
-	rc = pmic8xxx_kp_read(kp, data, reg, 1);
-	return rc;
-}
-
 static u8 pmic8xxx_col_state(struct pmic8xxx_kp *kp, u8 col)
 {
 	/* all keys pressed on that particular row? */
@@ -160,9 +135,9 @@ static u8 pmic8xxx_col_state(struct pmic8xxx_kp *kp, u8 col)
 static int pmic8xxx_chk_sync_read(struct pmic8xxx_kp *kp)
 {
 	int rc;
-	u8 scan_val;
+	unsigned int scan_val;
 
-	rc = pmic8xxx_kp_read_u8(kp, &scan_val, KEYP_SCAN);
+	rc = regmap_read(kp->regmap, KEYP_SCAN, &scan_val);
 	if (rc < 0) {
 		dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc);
 		return rc;
@@ -170,7 +145,7 @@ static int pmic8xxx_chk_sync_read(struct pmic8xxx_kp *kp)
 
 	scan_val |= 0x1;
 
-	rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN);
+	rc = regmap_write(kp->regmap, KEYP_SCAN, scan_val);
 	if (rc < 0) {
 		dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc);
 		return rc;
@@ -186,26 +161,24 @@ static int pmic8xxx_kp_read_data(struct pmic8xxx_kp *kp, u16 *state,
 					u16 data_reg, int read_rows)
 {
 	int rc, row;
-	u8 new_data[PM8XXX_MAX_ROWS];
+	unsigned int val;
 
-	rc = pmic8xxx_kp_read(kp, new_data, data_reg, read_rows);
-	if (rc)
-		return rc;
-
-	for (row = 0; row < kp->pdata->num_rows; row++) {
-		dev_dbg(kp->dev, "new_data[%d] = %d\n", row,
-					new_data[row]);
-		state[row] = pmic8xxx_col_state(kp, new_data[row]);
+	for (row = 0; row < read_rows; row++) {
+		rc = regmap_read(kp->regmap, data_reg, &val);
+		if (rc)
+			return rc;
+		dev_dbg(kp->dev, "%d = %d\n", row, val);
+		state[row] = pmic8xxx_col_state(kp, val);
 	}
 
-	return rc;
+	return 0;
 }
 
 static int pmic8xxx_kp_read_matrix(struct pmic8xxx_kp *kp, u16 *new_state,
 					 u16 *old_state)
 {
 	int rc, read_rows;
-	u8 scan_val;
+	unsigned int scan_val;
 
 	if (kp->pdata->num_rows < PM8XXX_MIN_ROWS)
 		read_rows = PM8XXX_MIN_ROWS;
@@ -235,14 +208,14 @@ static int pmic8xxx_kp_read_matrix(struct pmic8xxx_kp *kp, u16 *new_state,
 	/* 4 * 32KHz clocks */
 	udelay((4 * DIV_ROUND_UP(USEC_PER_SEC, KEYP_CLOCK_FREQ)) + 1);
 
-	rc = pmic8xxx_kp_read_u8(kp, &scan_val, KEYP_SCAN);
+	rc = regmap_read(kp->regmap, KEYP_SCAN, &scan_val);
 	if (rc < 0) {
 		dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc);
 		return rc;
 	}
 
 	scan_val &= 0xFE;
-	rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN);
+	rc = regmap_write(kp->regmap, KEYP_SCAN, scan_val);
 	if (rc < 0)
 		dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc);
 
@@ -378,10 +351,10 @@ static irqreturn_t pmic8xxx_kp_stuck_irq(int irq, void *data)
 static irqreturn_t pmic8xxx_kp_irq(int irq, void *data)
 {
 	struct pmic8xxx_kp *kp = data;
-	u8 ctrl_val, events;
+	unsigned int ctrl_val, events;
 	int rc;
 
-	rc = pmic8xxx_kp_read(kp, &ctrl_val, KEYP_CTRL, 1);
+	rc = regmap_read(kp->regmap, KEYP_CTRL, &ctrl_val);
 	if (rc < 0) {
 		dev_err(kp->dev, "failed to read keyp_ctrl register\n");
 		return IRQ_HANDLED;
@@ -420,7 +393,7 @@ static int pmic8xxx_kpd_init(struct pmic8xxx_kp *kp)
 
 	ctrl_val |= (bits << KEYP_CTRL_SCAN_ROWS_SHIFT);
 
-	rc = pmic8xxx_kp_write_u8(kp, ctrl_val, KEYP_CTRL);
+	rc = regmap_write(kp->regmap, KEYP_CTRL, ctrl_val);
 	if (rc < 0) {
 		dev_err(kp->dev, "Error writing KEYP_CTRL reg, rc=%d\n", rc);
 		return rc;
@@ -438,7 +411,7 @@ static int pmic8xxx_kpd_init(struct pmic8xxx_kp *kp)
 
 	scan_val |= (cycles << KEYP_SCAN_ROW_HOLD_SHIFT);
 
-	rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN);
+	rc = regmap_write(kp->regmap, KEYP_SCAN, scan_val);
 	if (rc)
 		dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc);
 
@@ -452,7 +425,7 @@ static int pmic8xxx_kp_enable(struct pmic8xxx_kp *kp)
 
 	kp->ctrl_reg |= KEYP_CTRL_KEYP_EN;
 
-	rc = pmic8xxx_kp_write_u8(kp, kp->ctrl_reg, KEYP_CTRL);
+	rc = regmap_write(kp->regmap, KEYP_CTRL, kp->ctrl_reg);
 	if (rc < 0)
 		dev_err(kp->dev, "Error writing KEYP_CTRL reg, rc=%d\n", rc);
 
@@ -465,7 +438,7 @@ static int pmic8xxx_kp_disable(struct pmic8xxx_kp *kp)
 
 	kp->ctrl_reg &= ~KEYP_CTRL_KEYP_EN;
 
-	rc = pmic8xxx_kp_write_u8(kp, kp->ctrl_reg, KEYP_CTRL);
+	rc = regmap_write(kp->regmap, KEYP_CTRL, kp->ctrl_reg);
 	if (rc < 0)
 		return rc;
 
@@ -503,7 +476,7 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
 	const struct matrix_keymap_data *keymap_data;
 	struct pmic8xxx_kp *kp;
 	int rc;
-	u8 ctrl_val;
+	unsigned int ctrl_val;
 
 	if (!pdata || !pdata->num_cols || !pdata->num_rows ||
 		pdata->num_cols > PM8XXX_MAX_COLS ||
@@ -547,6 +520,10 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
 	if (!kp)
 		return -ENOMEM;
 
+	kp->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!kp->regmap)
+		return -ENODEV;
+
 	platform_set_drvdata(pdev, kp);
 
 	kp->pdata	= pdata;
@@ -621,7 +598,7 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
 		return rc;
 	}
 
-	rc = pmic8xxx_kp_read_u8(kp, &ctrl_val, KEYP_CTRL);
+	rc = regmap_read(kp->regmap, KEYP_CTRL, &ctrl_val);
 	if (rc < 0) {
 		dev_err(&pdev->dev, "failed to read KEYP_CTRL register\n");
 		return rc;
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply related

* [PATCH v5 4/9] Input: pmic8xxx-keypad - Migrate to DT
From: Stephen Boyd @ 2014-03-04 18:34 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel, linux-arm-msm, linux-arm-kernel, Josh Cartwright,
	linux-input
In-Reply-To: <1393958088-1456-1-git-send-email-sboyd@codeaurora.org>

The driver is only supported on DT enabled platforms. Convert the
driver to DT so that it can probe properly.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 drivers/input/keyboard/pmic8xxx-keypad.c | 150 ++++++++++++++++++-------------
 include/linux/input/pmic8xxx-keypad.h    |  52 -----------
 2 files changed, 86 insertions(+), 116 deletions(-)
 delete mode 100644 include/linux/input/pmic8xxx-keypad.h

diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c
index 0efd11e16b7e..6ee813acc34d 100644
--- a/drivers/input/keyboard/pmic8xxx-keypad.c
+++ b/drivers/input/keyboard/pmic8xxx-keypad.c
@@ -20,8 +20,8 @@
 #include <linux/delay.h>
 #include <linux/mutex.h>
 #include <linux/regmap.h>
-
-#include <linux/input/pmic8xxx-keypad.h>
+#include <linux/of.h>
+#include <linux/input/matrix_keypad.h>
 
 #define PM8XXX_MAX_ROWS		18
 #define PM8XXX_MAX_COLS		8
@@ -84,7 +84,8 @@
 
 /**
  * struct pmic8xxx_kp - internal keypad data structure
- * @pdata - keypad platform data pointer
+ * @num_cols - number of columns of keypad
+ * @num_rows - number of row of keypad
  * @input - input device pointer for keypad
  * @regmap - regmap handle
  * @key_sense_irq - key press/release irq number
@@ -96,7 +97,8 @@
  * @ctrl_reg - control register value
  */
 struct pmic8xxx_kp {
-	const struct pm8xxx_keypad_platform_data *pdata;
+	unsigned int num_rows;
+	unsigned int num_cols;
 	struct input_dev *input;
 	struct regmap *regmap;
 	int key_sense_irq;
@@ -115,9 +117,9 @@ static u8 pmic8xxx_col_state(struct pmic8xxx_kp *kp, u8 col)
 {
 	/* all keys pressed on that particular row? */
 	if (col == 0x00)
-		return 1 << kp->pdata->num_cols;
+		return 1 << kp->num_cols;
 	else
-		return col & ((1 << kp->pdata->num_cols) - 1);
+		return col & ((1 << kp->num_cols) - 1);
 }
 
 /*
@@ -180,10 +182,10 @@ static int pmic8xxx_kp_read_matrix(struct pmic8xxx_kp *kp, u16 *new_state,
 	int rc, read_rows;
 	unsigned int scan_val;
 
-	if (kp->pdata->num_rows < PM8XXX_MIN_ROWS)
+	if (kp->num_rows < PM8XXX_MIN_ROWS)
 		read_rows = PM8XXX_MIN_ROWS;
 	else
-		read_rows = kp->pdata->num_rows;
+		read_rows = kp->num_rows;
 
 	pmic8xxx_chk_sync_read(kp);
 
@@ -227,13 +229,13 @@ static void __pmic8xxx_kp_scan_matrix(struct pmic8xxx_kp *kp, u16 *new_state,
 {
 	int row, col, code;
 
-	for (row = 0; row < kp->pdata->num_rows; row++) {
+	for (row = 0; row < kp->num_rows; row++) {
 		int bits_changed = new_state[row] ^ old_state[row];
 
 		if (!bits_changed)
 			continue;
 
-		for (col = 0; col < kp->pdata->num_cols; col++) {
+		for (col = 0; col < kp->num_cols; col++) {
 			if (!(bits_changed & (1 << col)))
 				continue;
 
@@ -259,9 +261,9 @@ static bool pmic8xxx_detect_ghost_keys(struct pmic8xxx_kp *kp, u16 *new_state)
 	u16 check, row_state;
 
 	check = 0;
-	for (row = 0; row < kp->pdata->num_rows; row++) {
+	for (row = 0; row < kp->num_rows; row++) {
 		row_state = (~new_state[row]) &
-				 ((1 << kp->pdata->num_cols) - 1);
+				 ((1 << kp->num_cols) - 1);
 
 		if (hweight16(row_state) > 1) {
 			if (found_first == -1)
@@ -369,8 +371,13 @@ static irqreturn_t pmic8xxx_kp_irq(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
-static int pmic8xxx_kpd_init(struct pmic8xxx_kp *kp)
+static int pmic8xxx_kpd_init(struct pmic8xxx_kp *kp,
+			     struct platform_device *pdev)
 {
+	const struct device_node *of_node = pdev->dev.of_node;
+	unsigned int scan_delay_ms;
+	unsigned int row_hold_ns;
+	unsigned int debounce_ms;
 	int bits, rc, cycles;
 	u8 scan_val = 0, ctrl_val = 0;
 	static const u8 row_bits[] = {
@@ -378,18 +385,18 @@ static int pmic8xxx_kpd_init(struct pmic8xxx_kp *kp)
 	};
 
 	/* Find column bits */
-	if (kp->pdata->num_cols < KEYP_CTRL_SCAN_COLS_MIN)
+	if (kp->num_cols < KEYP_CTRL_SCAN_COLS_MIN)
 		bits = 0;
 	else
-		bits = kp->pdata->num_cols - KEYP_CTRL_SCAN_COLS_MIN;
+		bits = kp->num_cols - KEYP_CTRL_SCAN_COLS_MIN;
 	ctrl_val = (bits & KEYP_CTRL_SCAN_COLS_BITS) <<
 		KEYP_CTRL_SCAN_COLS_SHIFT;
 
 	/* Find row bits */
-	if (kp->pdata->num_rows < KEYP_CTRL_SCAN_ROWS_MIN)
+	if (kp->num_rows < KEYP_CTRL_SCAN_ROWS_MIN)
 		bits = 0;
 	else
-		bits = row_bits[kp->pdata->num_rows - KEYP_CTRL_SCAN_ROWS_MIN];
+		bits = row_bits[kp->num_rows - KEYP_CTRL_SCAN_ROWS_MIN];
 
 	ctrl_val |= (bits << KEYP_CTRL_SCAN_ROWS_SHIFT);
 
@@ -399,15 +406,44 @@ static int pmic8xxx_kpd_init(struct pmic8xxx_kp *kp)
 		return rc;
 	}
 
-	bits = (kp->pdata->debounce_ms / 5) - 1;
+	if (of_property_read_u32(of_node, "scan-delay", &scan_delay_ms))
+		scan_delay_ms = MIN_SCAN_DELAY;
+
+	if (scan_delay_ms > MAX_SCAN_DELAY || scan_delay_ms < MIN_SCAN_DELAY ||
+		!is_power_of_2(scan_delay_ms)) {
+		dev_err(&pdev->dev, "invalid keypad scan time supplied\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(of_node, "row-hold", &row_hold_ns))
+		row_hold_ns = MIN_ROW_HOLD_DELAY;
+
+	if (row_hold_ns > MAX_ROW_HOLD_DELAY ||
+		row_hold_ns < MIN_ROW_HOLD_DELAY ||
+		((row_hold_ns % MIN_ROW_HOLD_DELAY) != 0)) {
+		dev_err(&pdev->dev, "invalid keypad row hold time supplied\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(of_node, "debounce", &debounce_ms))
+		debounce_ms = MIN_DEBOUNCE_TIME;
+
+	if (((debounce_ms % 5) != 0) ||
+		debounce_ms > MAX_DEBOUNCE_TIME ||
+		debounce_ms < MIN_DEBOUNCE_TIME) {
+		dev_err(&pdev->dev, "invalid debounce time supplied\n");
+		return -EINVAL;
+	}
+
+	bits = (debounce_ms / 5) - 1;
 
 	scan_val |= (bits << KEYP_SCAN_DBOUNCE_SHIFT);
 
-	bits = fls(kp->pdata->scan_delay_ms) - 1;
+	bits = fls(scan_delay_ms) - 1;
 	scan_val |= (bits << KEYP_SCAN_PAUSE_SHIFT);
 
 	/* Row hold time is a multiple of 32KHz cycles. */
-	cycles = (kp->pdata->row_hold_ns * KEYP_CLOCK_FREQ) / NSEC_PER_SEC;
+	cycles = (row_hold_ns * KEYP_CLOCK_FREQ) / NSEC_PER_SEC;
 
 	scan_val |= (cycles << KEYP_SCAN_ROW_HOLD_SHIFT);
 
@@ -459,6 +495,13 @@ static void pmic8xxx_kp_close(struct input_dev *dev)
 	pmic8xxx_kp_disable(kp);
 }
 
+static const struct of_device_id pm8xxx_match_table[] = {
+	{ .compatible = "qcom,pm8058-keypad" },
+	{ .compatible = "qcom,pm8921-keypad" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pm8xxx_match_table);
+
 /*
  * keypad controller should be initialized in the following sequence
  * only, otherwise it might get into FSM stuck state.
@@ -471,50 +514,27 @@ static void pmic8xxx_kp_close(struct input_dev *dev)
  */
 static int pmic8xxx_kp_probe(struct platform_device *pdev)
 {
-	const struct pm8xxx_keypad_platform_data *pdata =
-					dev_get_platdata(&pdev->dev);
-	const struct matrix_keymap_data *keymap_data;
+	unsigned int rows, cols;
+	bool repeat;
+	bool wakeup;
 	struct pmic8xxx_kp *kp;
 	int rc;
 	unsigned int ctrl_val;
 
-	if (!pdata || !pdata->num_cols || !pdata->num_rows ||
-		pdata->num_cols > PM8XXX_MAX_COLS ||
-		pdata->num_rows > PM8XXX_MAX_ROWS ||
-		pdata->num_cols < PM8XXX_MIN_COLS) {
-		dev_err(&pdev->dev, "invalid platform data\n");
-		return -EINVAL;
-	}
-
-	if (!pdata->scan_delay_ms ||
-		pdata->scan_delay_ms > MAX_SCAN_DELAY ||
-		pdata->scan_delay_ms < MIN_SCAN_DELAY ||
-		!is_power_of_2(pdata->scan_delay_ms)) {
-		dev_err(&pdev->dev, "invalid keypad scan time supplied\n");
-		return -EINVAL;
-	}
-
-	if (!pdata->row_hold_ns ||
-		pdata->row_hold_ns > MAX_ROW_HOLD_DELAY ||
-		pdata->row_hold_ns < MIN_ROW_HOLD_DELAY ||
-		((pdata->row_hold_ns % MIN_ROW_HOLD_DELAY) != 0)) {
-		dev_err(&pdev->dev, "invalid keypad row hold time supplied\n");
-		return -EINVAL;
-	}
+	rc = matrix_keypad_parse_of_params(&pdev->dev, &rows, &cols);
+	if (rc)
+		return rc;
 
-	if (!pdata->debounce_ms ||
-		((pdata->debounce_ms % 5) != 0) ||
-		pdata->debounce_ms > MAX_DEBOUNCE_TIME ||
-		pdata->debounce_ms < MIN_DEBOUNCE_TIME) {
-		dev_err(&pdev->dev, "invalid debounce time supplied\n");
+	if (cols > PM8XXX_MAX_COLS || rows > PM8XXX_MAX_ROWS ||
+		cols < PM8XXX_MIN_COLS) {
+		dev_err(&pdev->dev, "invalid platform data\n");
 		return -EINVAL;
 	}
 
-	keymap_data = pdata->keymap_data;
-	if (!keymap_data) {
-		dev_err(&pdev->dev, "no keymap data supplied\n");
-		return -EINVAL;
-	}
+	repeat = !of_property_read_bool(pdev->dev.of_node,
+					"linux,input-no-autorepeat");
+	wakeup = of_property_read_bool(pdev->dev.of_node,
+					"linux,keypad-wakeup");
 
 	kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL);
 	if (!kp)
@@ -526,7 +546,8 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, kp);
 
-	kp->pdata	= pdata;
+	kp->num_rows	= rows;
+	kp->num_cols	= cols;
 	kp->dev		= &pdev->dev;
 
 	kp->input = devm_input_allocate_device(&pdev->dev);
@@ -547,8 +568,8 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
 		return kp->key_stuck_irq;
 	}
 
-	kp->input->name = pdata->input_name ? : "PMIC8XXX keypad";
-	kp->input->phys = pdata->input_phys_device ? : "pmic8xxx_keypad/input0";
+	kp->input->name = "PMIC8XXX keypad";
+	kp->input->phys = "pmic8xxx_keypad/input0";
 
 	kp->input->id.bustype	= BUS_I2C;
 	kp->input->id.version	= 0x0001;
@@ -558,7 +579,7 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
 	kp->input->open		= pmic8xxx_kp_open;
 	kp->input->close	= pmic8xxx_kp_close;
 
-	rc = matrix_keypad_build_keymap(keymap_data, NULL,
+	rc = matrix_keypad_build_keymap(NULL, NULL,
 					PM8XXX_MAX_ROWS, PM8XXX_MAX_COLS,
 					kp->keycodes, kp->input);
 	if (rc) {
@@ -566,7 +587,7 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
 		return rc;
 	}
 
-	if (pdata->rep)
+	if (repeat)
 		__set_bit(EV_REP, kp->input->evbit);
 	input_set_capability(kp->input, EV_MSC, MSC_SCAN);
 
@@ -576,7 +597,7 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
 	memset(kp->keystate, 0xff, sizeof(kp->keystate));
 	memset(kp->stuckstate, 0xff, sizeof(kp->stuckstate));
 
-	rc = pmic8xxx_kpd_init(kp);
+	rc = pmic8xxx_kpd_init(kp, pdev);
 	if (rc < 0) {
 		dev_err(&pdev->dev, "unable to initialize keypad controller\n");
 		return rc;
@@ -612,7 +633,7 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
 		return rc;
 	}
 
-	device_init_wakeup(&pdev->dev, pdata->wakeup);
+	device_init_wakeup(&pdev->dev, wakeup);
 
 	return 0;
 }
@@ -665,9 +686,10 @@ static SIMPLE_DEV_PM_OPS(pm8xxx_kp_pm_ops,
 static struct platform_driver pmic8xxx_kp_driver = {
 	.probe		= pmic8xxx_kp_probe,
 	.driver		= {
-		.name = PM8XXX_KEYPAD_DEV_NAME,
+		.name = "pm8xxx-keypad",
 		.owner = THIS_MODULE,
 		.pm = &pm8xxx_kp_pm_ops,
+		.of_match_table = pm8xxx_match_table,
 	},
 };
 module_platform_driver(pmic8xxx_kp_driver);
diff --git a/include/linux/input/pmic8xxx-keypad.h b/include/linux/input/pmic8xxx-keypad.h
deleted file mode 100644
index 5f1e2f9ad959..000000000000
--- a/include/linux/input/pmic8xxx-keypad.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
- *
- * 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.
- */
-
-#ifndef __PMIC8XXX_KEYPAD_H__
-#define __PMIC8XXX_KEYPAD_H__
-
-#include <linux/input/matrix_keypad.h>
-
-#define PM8XXX_KEYPAD_DEV_NAME     "pm8xxx-keypad"
-
-/**
- * struct pm8xxx_keypad_platform_data - platform data for keypad
- * @keymap_data - matrix keymap data
- * @input_name - input device name
- * @input_phys_device - input device name
- * @num_cols - number of columns of keypad
- * @num_rows - number of row of keypad
- * @debounce_ms - debounce period in milliseconds
- * @scan_delay_ms - scan delay in milliseconds
- * @row_hold_ns - row hold period in nanoseconds
- * @wakeup - configure keypad as wakeup
- * @rep - enable or disable key repeat bit
- */
-struct pm8xxx_keypad_platform_data {
-	const struct matrix_keymap_data *keymap_data;
-
-	const char *input_name;
-	const char *input_phys_device;
-
-	unsigned int num_cols;
-	unsigned int num_rows;
-	unsigned int rows_gpio_start;
-	unsigned int cols_gpio_start;
-
-	unsigned int debounce_ms;
-	unsigned int scan_delay_ms;
-	unsigned int row_hold_ns;
-
-	bool wakeup;
-	bool rep;
-};
-
-#endif /*__PMIC8XXX_KEYPAD_H__ */
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply related

* [PATCH v5 5/9] Input: pmic8xxx-pwrkey - Migrate to DT
From: Stephen Boyd @ 2014-03-04 18:34 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel, linux-arm-msm, linux-arm-kernel, Josh Cartwright,
	linux-input
In-Reply-To: <1393958088-1456-1-git-send-email-sboyd@codeaurora.org>

The driver is only supported on DT enabled platforms. Convert the
driver to DT so that it can probe properly.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 drivers/input/misc/pmic8xxx-pwrkey.c  | 33 ++++++++++++++++++++-------------
 include/linux/input/pmic8xxx-pwrkey.h | 31 -------------------------------
 2 files changed, 20 insertions(+), 44 deletions(-)
 delete mode 100644 include/linux/input/pmic8xxx-pwrkey.h

diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c
index 0e1a05f95858..99a3587ff49f 100644
--- a/drivers/input/misc/pmic8xxx-pwrkey.c
+++ b/drivers/input/misc/pmic8xxx-pwrkey.c
@@ -19,8 +19,7 @@
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/log2.h>
-
-#include <linux/input/pmic8xxx-pwrkey.h>
+#include <linux/of.h>
 
 #define PON_CNTL_1 0x1C
 #define PON_CNTL_PULL_UP BIT(7)
@@ -79,6 +78,13 @@ static int pmic8xxx_pwrkey_resume(struct device *dev)
 static SIMPLE_DEV_PM_OPS(pm8xxx_pwr_key_pm_ops,
 		pmic8xxx_pwrkey_suspend, pmic8xxx_pwrkey_resume);
 
+static const struct of_device_id pm8xxx_pwr_key_id_table[] = {
+	{ .compatible = "qcom,pm8058-pwrkey" },
+	{ .compatible = "qcom,pm8921-pwrkey" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pm8xxx_pwr_key_id_table);
+
 static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
 {
 	struct input_dev *pwr;
@@ -89,15 +95,15 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
 	unsigned int pon_cntl;
 	struct regmap *regmap;
 	struct pmic8xxx_pwrkey *pwrkey;
-	const struct pm8xxx_pwrkey_platform_data *pdata =
-					dev_get_platdata(&pdev->dev);
+	u32 kpd_delay;
+	bool pull_up;
 
-	if (!pdata) {
-		dev_err(&pdev->dev, "power key platform data not supplied\n");
-		return -EINVAL;
-	}
+	if (of_property_read_u32(pdev->dev.of_node, "debounce", &kpd_delay))
+		kpd_delay = 0;
+
+	pull_up = of_property_read_bool(pdev->dev.of_node, "pull-up");
 
-	if (pdata->kpd_trigger_delay_us > 62500) {
+	if (kpd_delay > 62500) {
 		dev_err(&pdev->dev, "invalid power key trigger delay\n");
 		return -EINVAL;
 	}
@@ -125,7 +131,7 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
 	pwr->name = "pmic8xxx_pwrkey";
 	pwr->phys = "pmic8xxx_pwrkey/input0";
 
-	delay = (pdata->kpd_trigger_delay_us << 10) / USEC_PER_SEC;
+	delay = (kpd_delay << 10) / USEC_PER_SEC;
 	delay = 1 + ilog2(delay);
 
 	err = regmap_read(regmap, PON_CNTL_1, &pon_cntl);
@@ -136,7 +142,7 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
 
 	pon_cntl &= ~PON_CNTL_TRIG_DELAY_MASK;
 	pon_cntl |= (delay & PON_CNTL_TRIG_DELAY_MASK);
-	if (pdata->pull_up)
+	if (pull_up)
 		pon_cntl |= PON_CNTL_PULL_UP;
 	else
 		pon_cntl &= ~PON_CNTL_PULL_UP;
@@ -172,7 +178,7 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
 	}
 
 	platform_set_drvdata(pdev, pwrkey);
-	device_init_wakeup(&pdev->dev, pdata->wakeup);
+	device_init_wakeup(&pdev->dev, 1);
 
 	return 0;
 }
@@ -188,9 +194,10 @@ static struct platform_driver pmic8xxx_pwrkey_driver = {
 	.probe		= pmic8xxx_pwrkey_probe,
 	.remove		= pmic8xxx_pwrkey_remove,
 	.driver		= {
-		.name	= PM8XXX_PWRKEY_DEV_NAME,
+		.name	= "pm8xxx-pwrkey",
 		.owner	= THIS_MODULE,
 		.pm	= &pm8xxx_pwr_key_pm_ops,
+		.of_match_table = pm8xxx_pwr_key_id_table,
 	},
 };
 module_platform_driver(pmic8xxx_pwrkey_driver);
diff --git a/include/linux/input/pmic8xxx-pwrkey.h b/include/linux/input/pmic8xxx-pwrkey.h
deleted file mode 100644
index 6d2974e57109..000000000000
--- a/include/linux/input/pmic8xxx-pwrkey.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
- *
- * 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.
- */
-
-#ifndef __PMIC8XXX_PWRKEY_H__
-#define __PMIC8XXX_PWRKEY_H__
-
-#define PM8XXX_PWRKEY_DEV_NAME "pm8xxx-pwrkey"
-
-/**
- * struct pm8xxx_pwrkey_platform_data - platform data for pwrkey driver
- * @pull up:  power on register control for pull up/down configuration
- * @kpd_trigger_delay_us: time delay for power key state change interrupt
- *                  trigger.
- * @wakeup: configure power key as wakeup source
- */
-struct pm8xxx_pwrkey_platform_data  {
-	bool pull_up;
-	u32  kpd_trigger_delay_us;
-	u32  wakeup;
-};
-
-#endif /* __PMIC8XXX_PWRKEY_H__ */
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply related

* [PATCH v5 6/9] Input: pm8xxx-vibrator - Add DT match table
From: Stephen Boyd @ 2014-03-04 18:34 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel, linux-arm-msm, linux-arm-kernel, Josh Cartwright,
	linux-input
In-Reply-To: <1393958088-1456-1-git-send-email-sboyd@codeaurora.org>

The driver is only supported on DT enabled platforms. Convert the
driver to DT so that it can probe properly.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 drivers/input/misc/pm8xxx-vibrator.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/input/misc/pm8xxx-vibrator.c b/drivers/input/misc/pm8xxx-vibrator.c
index b88b7cbf93e2..5079bc54c3aa 100644
--- a/drivers/input/misc/pm8xxx-vibrator.c
+++ b/drivers/input/misc/pm8xxx-vibrator.c
@@ -141,6 +141,13 @@ static int pm8xxx_vib_play_effect(struct input_dev *dev, void *data,
 	return 0;
 }
 
+static const struct of_device_id pm8xxx_vib_id_table[] = {
+	{ .compatible = "qcom,pm8058-vib" },
+	{ .compatible = "qcom,pm8921-vib" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pm8xxx_vib_id_table);
+
 static int pm8xxx_vib_probe(struct platform_device *pdev)
 
 {
@@ -220,6 +227,7 @@ static struct platform_driver pm8xxx_vib_driver = {
 		.name	= "pm8xxx-vib",
 		.owner	= THIS_MODULE,
 		.pm	= &pm8xxx_vib_pm_ops,
+		.of_match_table = pm8xxx_vib_id_table,
 	},
 };
 module_platform_driver(pm8xxx_vib_driver);
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply related

* [PATCH v5 7/9] devicetree: bindings: Document PM8921/8058 keypads
From: Stephen Boyd @ 2014-03-04 18:34 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel, linux-arm-msm, linux-arm-kernel, Josh Cartwright,
	linux-input, devicetree
In-Reply-To: <1393958088-1456-1-git-send-email-sboyd@codeaurora.org>

Document the keypad device found on PM8921 and PM8058 PMICs.

Cc: <devicetree@vger.kernel.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 .../bindings/input/qcom,pm8xxx-keypad.txt          | 89 ++++++++++++++++++++++
 1 file changed, 89 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt

diff --git a/Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt b/Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt
new file mode 100644
index 000000000000..7d8cb92831d7
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt
@@ -0,0 +1,89 @@
+Qualcomm PM8xxx PMIC Keypad
+
+PROPERTIES
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: must be one of:
+		    "qcom,pm8058-keypad"
+		    "qcom,pm8921-keypad"
+
+- reg:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: address of keypad control register
+
+- interrupts:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: the first interrupt specifies the key sense interrupt
+		    and the second interrupt specifies the key stuck interrupt.
+		    The format of the specifier is defined by the binding
+		    document describing the node's interrupt parent.
+
+- linux,keymap:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: the linux keymap. More information can be found in
+		    input/matrix-keymap.txt.
+
+- linux,keypad-no-autorepeat:
+	Usage: optional
+	Value type: <bool>
+	Definition: don't enable autorepeat feature.
+
+- linux,keypad-wakeup:
+	Usage: optional
+	Value type: <bool>
+	Definition: use any event on keypad as wakeup event.
+
+- keypad,num-rows:
+	Usage: required
+	Value type: <u32>
+	Definition: number of rows in the keymap. More information can be found
+		    in input/matrix-keymap.txt.
+
+- keypad,num-columns:
+	Usage: required
+	Value type: <u32>
+	Definition: number of columns in the keymap. More information can be
+		    found in input/matrix-keymap.txt.
+
+- debounce:
+	Usage: optional
+	Value type: <u32>
+	Definition: time in microseconds that key must be pressed or release
+		    for key sense interrupt to trigger.
+
+- scan-delay:
+	Usage: optional
+	Value type: <u32>
+	Definition: time in microseconds to pause between successive scans
+		    of the matrix array.
+
+- row-hold:
+	Usage: optional
+	Value type: <u32>
+	Definition: time in nanoseconds to pause between scans of each row in
+		    the matrix array.
+
+EXAMPLE
+
+	keypad@148 {
+		compatible = "qcom,pm8921-keypad";
+		reg = <0x148>;
+		interrupt-parent = <&pmicintc>;
+		interrupts = <74 1>, <75 1>;
+		linux,keymap = <
+			MATRIX_KEY(0, 0, KEY_VOLUMEUP)
+			MATRIX_KEY(0, 1, KEY_VOLUMEDOWN)
+			MATRIX_KEY(0, 2, KEY_CAMERA_FOCUS)
+			MATRIX_KEY(0, 3, KEY_CAMERA)
+			>;
+		keypad,num-rows = <1>;
+		keypad,num-columns = <5>;
+		debounce = <15>;
+		scan-delay = <32>;
+		row-hold = <91500>;
+	};
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply related

* [PATCH v5 8/9] devicetree: bindings: Document PM8921/8058 power keys
From: Stephen Boyd @ 2014-03-04 18:34 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel, linux-arm-msm, linux-arm-kernel, Josh Cartwright,
	linux-input, devicetree
In-Reply-To: <1393958088-1456-1-git-send-email-sboyd@codeaurora.org>

Document the power key found on PM8921 and PM8058 PMICs.

Cc: <devicetree@vger.kernel.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 .../bindings/input/qcom,pm8xxx-pwrkey.txt          | 46 ++++++++++++++++++++++
 1 file changed, 46 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/qcom,pm8xxx-pwrkey.txt

diff --git a/Documentation/devicetree/bindings/input/qcom,pm8xxx-pwrkey.txt b/Documentation/devicetree/bindings/input/qcom,pm8xxx-pwrkey.txt
new file mode 100644
index 000000000000..588536cc96ed
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/qcom,pm8xxx-pwrkey.txt
@@ -0,0 +1,46 @@
+Qualcomm PM8xxx PMIC Power Key
+
+PROPERTIES
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: must be one of:
+		    "qcom,pm8058-pwrkey"
+		    "qcom,pm8921-pwrkey"
+
+- reg:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: address of power key control register
+
+- interrupts:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: the first interrupt specifies the key release interrupt
+		    and the second interrupt specifies the key press interrupt.
+		    The format of the specifier is defined by the binding
+		    document describing the node's interrupt parent.
+
+- debounce:
+	Usage: optional
+	Value type: <u32>
+	Definition: time in microseconds that key must be pressed or release
+		    for state change interrupt to trigger.
+
+- pull-up:
+	Usage: optional
+	Value type: <empty>
+	Definition: presence of this property indicates that the KPDPWR_N pin
+		    should be configured for pull up.
+
+EXAMPLE
+
+	pwrkey@1c {
+		compatible = "qcom,pm8921-pwrkey";
+		reg = <0x1c>;
+		interrupt-parent = <&pmicintc>;
+		interrupts = <50 1>, <51 1>;
+		debounce = <15625>;
+		pull-up;
+	};
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply related

* [PATCH v5 9/9] devicetree: bindings: Document PM8921/8058 vibrators
From: Stephen Boyd @ 2014-03-04 18:34 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel, linux-arm-msm, linux-arm-kernel, Josh Cartwright,
	linux-input, devicetree
In-Reply-To: <1393958088-1456-1-git-send-email-sboyd@codeaurora.org>

Document the vibration device found on PM8921 and PM8058 PMICs.

Cc: <devicetree@vger.kernel.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 .../devicetree/bindings/input/qcom,pm8xxx-vib.txt  | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/qcom,pm8xxx-vib.txt

diff --git a/Documentation/devicetree/bindings/input/qcom,pm8xxx-vib.txt b/Documentation/devicetree/bindings/input/qcom,pm8xxx-vib.txt
new file mode 100644
index 000000000000..4ed467b1e402
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/qcom,pm8xxx-vib.txt
@@ -0,0 +1,22 @@
+Qualcomm PM8xxx PMIC Vibrator
+
+PROPERTIES
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: must be one of:
+		    "qcom,pm8058-vib"
+		    "qcom,pm8921-vib"
+
+- reg:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: address of vibration control register
+
+EXAMPLE
+
+	vibrator@4a {
+		compatible = "qcom,pm8058-vib";
+		reg = <0x4a>;
+	};
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply related

* [PATCH] touchscreen: tps6507x-ts: only poll while touch event is occurring
From: Jon Ringle @ 2014-03-04 20:04 UTC (permalink / raw)
  To: dmitry.torokhov, linux-input, linux-kernel; +Cc: Jon Ringle

We only need to poll for touch events after an interrupt occurs due to the
user touching the screen. We continue to poll until the user stops touching
the screen

Signed-off-by: Jon Ringle <jringle@gridpoint.com>
---
 drivers/input/touchscreen/tps6507x-ts.c | 191 +++++++++++++++++++++++++-------
 include/linux/mfd/tps6507x.h            |   1 +
 2 files changed, 153 insertions(+), 39 deletions(-)

diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c
index 94cde2c..fa1fb24 100644
--- a/drivers/input/touchscreen/tps6507x-ts.c
+++ b/drivers/input/touchscreen/tps6507x-ts.c
@@ -17,11 +17,14 @@
 #include <linux/workqueue.h>
 #include <linux/slab.h>
 #include <linux/input.h>
-#include <linux/input-polldev.h>
 #include <linux/platform_device.h>
 #include <linux/mfd/tps6507x.h>
 #include <linux/input/tps6507x-ts.h>
 #include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>

 #define TSC_DEFAULT_POLL_PERIOD 30 /* ms */
 #define TPS_DEFAULT_MIN_PRESSURE 0x30
@@ -39,13 +42,17 @@ struct ts_event {
 };

 struct tps6507x_ts {
+       struct input_dev        *input_dev;
        struct device           *dev;
-       struct input_polled_dev *poll_dev;
        struct tps6507x_dev     *mfd;
        char                    phys[32];
+       struct delayed_work     work;
        struct ts_event         tc;
+       int                     irq;
+       unsigned long           poll_period;    /* ms */
        u16                     min_pressure;
        bool                    pendown;
+       int                     open_count;
 };

 static int tps6507x_read_u8(struct tps6507x_ts *tsc, u8 reg, u8 *data)
@@ -155,13 +162,20 @@ static s32 tps6507x_adc_standby(struct tps6507x_ts *tsc)
        return ret;
 }

-static void tps6507x_ts_poll(struct input_polled_dev *poll_dev)
+static void tps6507x_ts_handler(struct work_struct *work)
 {
-       struct tps6507x_ts *tsc = poll_dev->private;
-       struct input_dev *input_dev = poll_dev->input;
+       struct tps6507x_ts *tsc =  container_of(work, struct tps6507x_ts, work.work);
+       struct input_dev *input_dev = tsc->input_dev;
        bool pendown;
+       int schd;
+       int poll = 0;
        s32 ret;

+       if (tsc->irq) {
+               // Disable the touch interrupt
+               tps6507x_write_u8(tsc, TPS6507X_REG_INT, 0);
+       }
+
        ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_PRESSURE,
                                      &tsc->tc.pressure);
        if (ret)
@@ -178,9 +192,11 @@ static void tps6507x_ts_poll(struct input_polled_dev *poll_dev)
        }

        if (pendown) {
+               int report_pendown = 0;

                if (!tsc->pendown) {
                        dev_dbg(tsc->dev, "DOWN\n");
+                       report_pendown = 1;
                        input_report_key(input_dev, BTN_TOUCH, 1);
                } else
                        dev_dbg(tsc->dev, "still down\n");
@@ -195,15 +211,89 @@ static void tps6507x_ts_poll(struct input_polled_dev *poll_dev)
                if (ret)
                        goto done;

-               input_report_abs(input_dev, ABS_X, tsc->tc.x);
-               input_report_abs(input_dev, ABS_Y, tsc->tc.y);
-               input_report_abs(input_dev, ABS_PRESSURE, tsc->tc.pressure);
-               input_sync(input_dev);
-               tsc->pendown = true;
+               if (tsc->tc.x && tsc->tc.y) {
+                       if (report_pendown)
+                               input_report_key(input_dev, BTN_TOUCH, 1);
+
+                       input_report_abs(input_dev, ABS_X, tsc->tc.x);
+                       input_report_abs(input_dev, ABS_Y, tsc->tc.y);
+                       input_report_abs(input_dev, ABS_PRESSURE, tsc->tc.pressure);
+                       input_sync(input_dev);
+                       tsc->pendown = true;
+               } else {
+                       dev_dbg(tsc->dev, "discarding bogus read x=%d, y=%d, pressure=%d\n", tsc->tc.x, tsc->tc.y, tsc->tc.pressure);
+               }
+               poll = 1;
        }

 done:
        tps6507x_adc_standby(tsc);
+       if (tsc->irq && !poll) {
+               // Re-enable the interrupt
+               tps6507x_write_u8(tsc, TPS6507X_REG_INT, TPS6507X_REG_MASK_TSC);
+       } else {
+               /* always poll if not using interrupts */
+               schd = schedule_delayed_work(&tsc->work,
+                                            msecs_to_jiffies(tsc->poll_period));
+       }
+}
+
+static irqreturn_t tps6507x_ts_irq(int irq, void * dev_id)
+{
+       struct tps6507x_ts * tsc = (struct tps6507x_ts *)dev_id;
+
+       schedule_delayed_work(&tsc->work, 0);
+
+       return IRQ_HANDLED;
+}
+
+static int tps6507x_ts_open(struct input_dev *input_dev)
+{
+       int ret;
+       struct tps6507x_ts *tsc = input_get_drvdata(input_dev);
+
+       tsc->open_count++;
+
+//     printk("%s: %s\n", __func__, comm);
+       dump_stack();
+
+       if (tsc->irq) {
+
+               tps6507x_write_u8(tsc, TPS6507X_REG_INT, 0);
+               tps6507x_adc_standby(tsc);
+
+               ret = request_irq(tsc->irq,tps6507x_ts_irq, 0, "TPS6507x",tsc);
+
+               if(ret < 0) {
+                       tsc->open_count--;
+                       dev_err(tsc->dev, "cannot register irq");
+                       return ret;
+               }
+               // Enable the touch event interrupt
+               tps6507x_write_u8(tsc, TPS6507X_REG_INT, TPS6507X_REG_MASK_TSC);
+       } else {
+               ret = schedule_delayed_work(&tsc->work,
+                                           msecs_to_jiffies(tsc->poll_period));
+
+               if (!ret) {
+                       tsc->open_count--;
+                       dev_err(tsc->dev, "schedule failed");
+                       return -ENOMEM;
+               }
+       }
+
+       return 0;
+}
+
+static void tps6507x_ts_close(struct input_dev *input_dev)
+{
+       struct tps6507x_ts *tsc = input_get_drvdata(input_dev);
+
+       tsc->open_count--;
+
+       if(tsc->irq) {
+               free_irq(tsc->irq,tsc);
+       }
 }

 static int tps6507x_ts_probe(struct platform_device *pdev)
@@ -212,8 +302,8 @@ static int tps6507x_ts_probe(struct platform_device *pdev)
        const struct tps6507x_board *tps_board;
        const struct touchscreen_init_data *init_data;
        struct tps6507x_ts *tsc;
-       struct input_polled_dev *poll_dev;
        struct input_dev *input_dev;
+       int schd;
        int error;

        /*
@@ -239,29 +329,18 @@ static int tps6507x_ts_probe(struct platform_device *pdev)
                return -ENOMEM;
        }

+       tps6507x_dev->ts = tsc;
        tsc->mfd = tps6507x_dev;
        tsc->dev = tps6507x_dev->dev;
-       tsc->min_pressure = init_data ?
-                       init_data->min_pressure : TPS_DEFAULT_MIN_PRESSURE;
-
-       snprintf(tsc->phys, sizeof(tsc->phys),
-                "%s/input0", dev_name(tsc->dev));
-
-       poll_dev = input_allocate_polled_device();
-       if (!poll_dev) {
-               dev_err(tsc->dev, "Failed to allocate polled input device.\n");
+       input_dev = input_allocate_device();
+       if (!input_dev) {
+               dev_err(tsc->dev, "Failed to allocate input device.\n");
                error = -ENOMEM;
                goto err_free_mem;
        }

-       tsc->poll_dev = poll_dev;
-
-       poll_dev->private = tsc;
-       poll_dev->poll = tps6507x_ts_poll;
-       poll_dev->poll_interval = init_data ?
-                       init_data->poll_period : TSC_DEFAULT_POLL_PERIOD;
-
-       input_dev = poll_dev->input;
+       input_dev->open = tps6507x_ts_open;
+       input_dev->close = tps6507x_ts_close;
        input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
        input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);

@@ -270,42 +349,76 @@ static int tps6507x_ts_probe(struct platform_device *pdev)
        input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_10BIT, 0, 0);

        input_dev->name = "TPS6507x Touchscreen";
-       input_dev->phys = tsc->phys;
-       input_dev->dev.parent = tsc->dev;
        input_dev->id.bustype = BUS_I2C;
+       input_dev->dev.parent = tsc->dev;
+
+       snprintf(tsc->phys, sizeof(tsc->phys),
+                "%s/input0", dev_name(tsc->dev));
+       input_dev->phys = tsc->phys;
+
+       dev_dbg(tsc->dev, "device: %s\n", input_dev->phys);
+
+       input_set_drvdata(input_dev, tsc);
+
+       tsc->input_dev = input_dev;
+
+       INIT_DELAYED_WORK(&tsc->work, tps6507x_ts_handler);
        if (init_data) {
+               tsc->poll_period = init_data->poll_period;
+               tsc->irq = gpio_to_irq(init_data->irq_pin);
+               tsc->min_pressure = init_data->min_pressure;
                input_dev->id.vendor = init_data->vendor;
                input_dev->id.product = init_data->product;
                input_dev->id.version = init_data->version;
+       } else {
+               tsc->poll_period = TSC_DEFAULT_POLL_PERIOD;
+               tsc->min_pressure = TPS_DEFAULT_MIN_PRESSURE;
        }

        error = tps6507x_adc_standby(tsc);
        if (error)
-               goto err_free_polled_dev;
+               goto err_free_dev;

-       error = input_register_polled_device(poll_dev);
+       error = input_register_device(input_dev);
        if (error)
-               goto err_free_polled_dev;
+               goto err_free_dev;
+
+       if (!tsc->irq) {
+               schd = schedule_delayed_work(&tsc->work,
+                                            msecs_to_jiffies(tsc->poll_period));
+
+               if (!schd) {
+                       dev_err(tsc->dev, "schedule failed");
+                       goto err_cancel_work;
+               }
+       }

        platform_set_drvdata(pdev, tsc);

        return 0;

-err_free_polled_dev:
-       input_free_polled_device(poll_dev);
+err_cancel_work:
+       cancel_delayed_work_sync(&tsc->work);
+err_free_dev:
+       input_free_device(input_dev);
 err_free_mem:
        kfree(tsc);
+       tps6507x_dev->ts = NULL;
+
        return error;
 }

 static int tps6507x_ts_remove(struct platform_device *pdev)
 {
-       struct tps6507x_ts *tsc = platform_get_drvdata(pdev);
-       struct input_polled_dev *poll_dev = tsc->poll_dev;
+       struct tps6507x_dev *tps6507x_dev = platform_get_drvdata(pdev);
+       struct tps6507x_ts *tsc = tps6507x_dev->ts;
+       struct input_dev *input_dev = tsc->input_dev;
+
+       cancel_delayed_work_sync(&tsc->work);

-       input_unregister_polled_device(poll_dev);
-       input_free_polled_device(poll_dev);
+       input_unregister_device(input_dev);

+       tps6507x_dev->ts = NULL;
        kfree(tsc);

        return 0;
diff --git a/include/linux/mfd/tps6507x.h b/include/linux/mfd/tps6507x.h
index c2ae569..c923e486 100644
--- a/include/linux/mfd/tps6507x.h
+++ b/include/linux/mfd/tps6507x.h
@@ -163,6 +163,7 @@ struct tps6507x_dev {

        /* Client devices */
        struct tps6507x_pmic *pmic;
+       struct tps6507x_ts *ts;
 };

 #endif /*  __LINUX_MFD_TPS6507X_H */
--
1.8.5.4


The information contained in this transmission may contain confidential information.  If the reader of this message is not the intended recipient, you are hereby notified that any review, dissemination, distribution or duplication of this communication is strictly prohibited.  If you are not the intended recipient, please contact the sender by reply email and destroy all copies of the original message.

^ permalink raw reply related

* [PATCH v2] touchscreen: tps6507x-ts: only poll while touch event is occurring
From: Jon Ringle @ 2014-03-04 20:19 UTC (permalink / raw)
  To: dmitry.torokhov, linux-input, linux-kernel; +Cc: Jon Ringle

We only need to poll for touch events after an interrupt occurs due to the
user touching the screen. We continue to poll until the user stops touching
the screen

Signed-off-by: Jon Ringle <jringle@gridpoint.com>
---
 drivers/input/touchscreen/tps6507x-ts.c | 188 +++++++++++++++++++++++++-------
 include/linux/input/tps6507x-ts.h       |   1 +
 include/linux/mfd/tps6507x.h            |   1 +
 3 files changed, 151 insertions(+), 39 deletions(-)

diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c
index 94cde2c..24043ff 100644
--- a/drivers/input/touchscreen/tps6507x-ts.c
+++ b/drivers/input/touchscreen/tps6507x-ts.c
@@ -17,11 +17,14 @@
 #include <linux/workqueue.h>
 #include <linux/slab.h>
 #include <linux/input.h>
-#include <linux/input-polldev.h>
 #include <linux/platform_device.h>
 #include <linux/mfd/tps6507x.h>
 #include <linux/input/tps6507x-ts.h>
 #include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>

 #define TSC_DEFAULT_POLL_PERIOD 30 /* ms */
 #define TPS_DEFAULT_MIN_PRESSURE 0x30
@@ -39,13 +42,17 @@ struct ts_event {
 };

 struct tps6507x_ts {
+       struct input_dev        *input_dev;
        struct device           *dev;
-       struct input_polled_dev *poll_dev;
        struct tps6507x_dev     *mfd;
        char                    phys[32];
+       struct delayed_work     work;
        struct ts_event         tc;
+       int                     irq;
+       unsigned long           poll_period;    /* ms */
        u16                     min_pressure;
        bool                    pendown;
+       int                     open_count;
 };

 static int tps6507x_read_u8(struct tps6507x_ts *tsc, u8 reg, u8 *data)
@@ -155,13 +162,20 @@ static s32 tps6507x_adc_standby(struct tps6507x_ts *tsc)
        return ret;
 }

-static void tps6507x_ts_poll(struct input_polled_dev *poll_dev)
+static void tps6507x_ts_handler(struct work_struct *work)
 {
-       struct tps6507x_ts *tsc = poll_dev->private;
-       struct input_dev *input_dev = poll_dev->input;
+       struct tps6507x_ts *tsc =  container_of(work, struct tps6507x_ts, work.work);
+       struct input_dev *input_dev = tsc->input_dev;
        bool pendown;
+       int schd;
+       int poll = 0;
        s32 ret;

+       if (tsc->irq) {
+               // Disable the touch interrupt
+               tps6507x_write_u8(tsc, TPS6507X_REG_INT, 0);
+       }
+
        ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_PRESSURE,
                                      &tsc->tc.pressure);
        if (ret)
@@ -178,9 +192,11 @@ static void tps6507x_ts_poll(struct input_polled_dev *poll_dev)
        }

        if (pendown) {
+               int report_pendown = 0;

                if (!tsc->pendown) {
                        dev_dbg(tsc->dev, "DOWN\n");
+                       report_pendown = 1;
                        input_report_key(input_dev, BTN_TOUCH, 1);
                } else
                        dev_dbg(tsc->dev, "still down\n");
@@ -195,15 +211,86 @@ static void tps6507x_ts_poll(struct input_polled_dev *poll_dev)
                if (ret)
                        goto done;

-               input_report_abs(input_dev, ABS_X, tsc->tc.x);
-               input_report_abs(input_dev, ABS_Y, tsc->tc.y);
-               input_report_abs(input_dev, ABS_PRESSURE, tsc->tc.pressure);
-               input_sync(input_dev);
-               tsc->pendown = true;
+               if (tsc->tc.x && tsc->tc.y) {
+                       if (report_pendown)
+                               input_report_key(input_dev, BTN_TOUCH, 1);
+
+                       input_report_abs(input_dev, ABS_X, tsc->tc.x);
+                       input_report_abs(input_dev, ABS_Y, tsc->tc.y);
+                       input_report_abs(input_dev, ABS_PRESSURE, tsc->tc.pressure);
+                       input_sync(input_dev);
+                       tsc->pendown = true;
+               } else {
+                       dev_dbg(tsc->dev, "discarding bogus read x=%d, y=%d, pressure=%d\n", tsc->tc.x, tsc->tc.y, tsc->tc.pressure);
+               }
+               poll = 1;
        }

 done:
        tps6507x_adc_standby(tsc);
+       if (tsc->irq && !poll) {
+               // Re-enable the interrupt
+               tps6507x_write_u8(tsc, TPS6507X_REG_INT, TPS6507X_REG_MASK_TSC);
+       } else {
+               /* always poll if not using interrupts */
+               schd = schedule_delayed_work(&tsc->work,
+                                            msecs_to_jiffies(tsc->poll_period));
+       }
+}
+
+static irqreturn_t tps6507x_ts_irq(int irq, void * dev_id)
+{
+       struct tps6507x_ts * tsc = (struct tps6507x_ts *)dev_id;
+
+       schedule_delayed_work(&tsc->work, 0);
+
+       return IRQ_HANDLED;
+}
+
+static int tps6507x_ts_open(struct input_dev *input_dev)
+{
+       int ret;
+       struct tps6507x_ts *tsc = input_get_drvdata(input_dev);
+
+       tsc->open_count++;
+
+       if (tsc->irq) {
+
+               tps6507x_write_u8(tsc, TPS6507X_REG_INT, 0);
+               tps6507x_adc_standby(tsc);
+
+               ret = request_irq(tsc->irq,tps6507x_ts_irq, 0, "TPS6507x",tsc);
+
+               if(ret < 0) {
+                       tsc->open_count--;
+                       dev_err(tsc->dev, "cannot register irq");
+                       return ret;
+               }
+               // Enable the touch event interrupt
+               tps6507x_write_u8(tsc, TPS6507X_REG_INT, TPS6507X_REG_MASK_TSC);
+       } else {
+               ret = schedule_delayed_work(&tsc->work,
+                                           msecs_to_jiffies(tsc->poll_period));
+
+               if (!ret) {
+                       tsc->open_count--;
+                       dev_err(tsc->dev, "schedule failed");
+                       return -ENOMEM;
+               }
+       }
+
+       return 0;
+}
+
+static void tps6507x_ts_close(struct input_dev *input_dev)
+{
+       struct tps6507x_ts *tsc = input_get_drvdata(input_dev);
+
+       tsc->open_count--;
+
+       if(tsc->irq) {
+               free_irq(tsc->irq,tsc);
+       }
 }

 static int tps6507x_ts_probe(struct platform_device *pdev)
@@ -212,8 +299,8 @@ static int tps6507x_ts_probe(struct platform_device *pdev)
        const struct tps6507x_board *tps_board;
        const struct touchscreen_init_data *init_data;
        struct tps6507x_ts *tsc;
-       struct input_polled_dev *poll_dev;
        struct input_dev *input_dev;
+       int schd;
        int error;

        /*
@@ -239,29 +326,18 @@ static int tps6507x_ts_probe(struct platform_device *pdev)
                return -ENOMEM;
        }

+       tps6507x_dev->ts = tsc;
        tsc->mfd = tps6507x_dev;
        tsc->dev = tps6507x_dev->dev;
-       tsc->min_pressure = init_data ?
-                       init_data->min_pressure : TPS_DEFAULT_MIN_PRESSURE;
-
-       snprintf(tsc->phys, sizeof(tsc->phys),
-                "%s/input0", dev_name(tsc->dev));
-
-       poll_dev = input_allocate_polled_device();
-       if (!poll_dev) {
-               dev_err(tsc->dev, "Failed to allocate polled input device.\n");
+       input_dev = input_allocate_device();
+       if (!input_dev) {
+               dev_err(tsc->dev, "Failed to allocate input device.\n");
                error = -ENOMEM;
                goto err_free_mem;
        }

-       tsc->poll_dev = poll_dev;
-
-       poll_dev->private = tsc;
-       poll_dev->poll = tps6507x_ts_poll;
-       poll_dev->poll_interval = init_data ?
-                       init_data->poll_period : TSC_DEFAULT_POLL_PERIOD;
-
-       input_dev = poll_dev->input;
+       input_dev->open = tps6507x_ts_open;
+       input_dev->close = tps6507x_ts_close;
        input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
        input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);

@@ -270,42 +346,76 @@ static int tps6507x_ts_probe(struct platform_device *pdev)
        input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_10BIT, 0, 0);

        input_dev->name = "TPS6507x Touchscreen";
-       input_dev->phys = tsc->phys;
-       input_dev->dev.parent = tsc->dev;
        input_dev->id.bustype = BUS_I2C;
+       input_dev->dev.parent = tsc->dev;
+
+       snprintf(tsc->phys, sizeof(tsc->phys),
+                "%s/input0", dev_name(tsc->dev));
+       input_dev->phys = tsc->phys;
+
+       dev_dbg(tsc->dev, "device: %s\n", input_dev->phys);
+
+       input_set_drvdata(input_dev, tsc);
+
+       tsc->input_dev = input_dev;
+
+       INIT_DELAYED_WORK(&tsc->work, tps6507x_ts_handler);
        if (init_data) {
+               tsc->poll_period = init_data->poll_period;
+               tsc->irq = gpio_to_irq(init_data->irq_pin);
+               tsc->min_pressure = init_data->min_pressure;
                input_dev->id.vendor = init_data->vendor;
                input_dev->id.product = init_data->product;
                input_dev->id.version = init_data->version;
+       } else {
+               tsc->poll_period = TSC_DEFAULT_POLL_PERIOD;
+               tsc->min_pressure = TPS_DEFAULT_MIN_PRESSURE;
        }

        error = tps6507x_adc_standby(tsc);
        if (error)
-               goto err_free_polled_dev;
+               goto err_free_dev;

-       error = input_register_polled_device(poll_dev);
+       error = input_register_device(input_dev);
        if (error)
-               goto err_free_polled_dev;
+               goto err_free_dev;
+
+       if (!tsc->irq) {
+               schd = schedule_delayed_work(&tsc->work,
+                                            msecs_to_jiffies(tsc->poll_period));
+
+               if (!schd) {
+                       dev_err(tsc->dev, "schedule failed");
+                       goto err_cancel_work;
+               }
+       }

        platform_set_drvdata(pdev, tsc);

        return 0;

-err_free_polled_dev:
-       input_free_polled_device(poll_dev);
+err_cancel_work:
+       cancel_delayed_work_sync(&tsc->work);
+err_free_dev:
+       input_free_device(input_dev);
 err_free_mem:
        kfree(tsc);
+       tps6507x_dev->ts = NULL;
+
        return error;
 }

 static int tps6507x_ts_remove(struct platform_device *pdev)
 {
-       struct tps6507x_ts *tsc = platform_get_drvdata(pdev);
-       struct input_polled_dev *poll_dev = tsc->poll_dev;
+       struct tps6507x_dev *tps6507x_dev = platform_get_drvdata(pdev);
+       struct tps6507x_ts *tsc = tps6507x_dev->ts;
+       struct input_dev *input_dev = tsc->input_dev;
+
+       cancel_delayed_work_sync(&tsc->work);

-       input_unregister_polled_device(poll_dev);
-       input_free_polled_device(poll_dev);
+       input_unregister_device(input_dev);

+       tps6507x_dev->ts = NULL;
        kfree(tsc);

        return 0;
diff --git a/include/linux/input/tps6507x-ts.h b/include/linux/input/tps6507x-ts.h
index b433df8..3c5aa7b 100644
--- a/include/linux/input/tps6507x-ts.h
+++ b/include/linux/input/tps6507x-ts.h
@@ -14,6 +14,7 @@
 /* Board specific touch screen initial values */
 struct touchscreen_init_data {
        int     poll_period;    /* ms */
+       int     irq_pin;
        __u16   min_pressure;   /* min reading to be treated as a touch */
        __u16   vendor;
        __u16   product;
diff --git a/include/linux/mfd/tps6507x.h b/include/linux/mfd/tps6507x.h
index c2ae569..c923e486 100644
--- a/include/linux/mfd/tps6507x.h
+++ b/include/linux/mfd/tps6507x.h
@@ -163,6 +163,7 @@ struct tps6507x_dev {

        /* Client devices */
        struct tps6507x_pmic *pmic;
+       struct tps6507x_ts *ts;
 };

 #endif /*  __LINUX_MFD_TPS6507X_H */
--
1.8.5.4


The information contained in this transmission may contain confidential information.  If the reader of this message is not the intended recipient, you are hereby notified that any review, dissemination, distribution or duplication of this communication is strictly prohibited.  If you are not the intended recipient, please contact the sender by reply email and destroy all copies of the original message.

^ permalink raw reply related

* Re: Re: [PATCH 6/7] regulator: AXP20x: Add support for regulators subsystem
From: Carlo Caione @ 2014-03-04 20:56 UTC (permalink / raw)
  To: broonie-DgEjT+Ai2ygdnm+yROfE0A
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	hdegoede-H+wXaHxf7aLQT0dZR+AlfA, emilio-0Z03zUJReD5OxF6Tv1QG9Q,
	wens-jdAy2FN1RRM, sameo-VuQAYsv1563Yd54FQh9/CA,
	lee.jones-QSEj5FYQhm4dnm+yROfE0A,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	lgirdwood-Re5JQEeQqe8AvxtiuMwx3w,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <20140303015616.GN2411-GFdadSzt00ze9xe1eoZjHA@public.gmane.org>

On Mon, Mar 3, 2014 at 2:56 AM, Mark Brown <broonie-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On Sat, Mar 01, 2014 at 05:45:51PM +0100, Carlo Caione wrote:
>
>> index d59c826..0cef101 100644
>> --- a/arch/arm/configs/sunxi_defconfig
>> +++ b/arch/arm/configs/sunxi_defconfig
>> @@ -69,3 +69,4 @@ CONFIG_NFS_FS=y
>>  CONFIG_ROOT_NFS=y
>>  CONFIG_NLS=y
>>  CONFIG_PRINTK_TIME=y
>> +CONFIG_REGULATOR_AXP20X=y
>
> If you want to update the defconfig do it in a separate patch.

Ok, I'll split the patch.

>> +static int axp20x_set_suspend_voltage(struct regulator_dev *rdev, int uV)
>> +{
>> +     return regulator_set_voltage_sel_regmap(rdev, 0);
>> +}

Right. I'll fix it in v2.

> I'm not entirely clear what this is supposed to do but it's broken - it
> both ignores the voltage that is passed in and immediately writes to the
> normal voltage select register which will presumably distrupt the system.
>
>> +static int axp20x_io_enable_regmap(struct regulator_dev *rdev)
>> +{
>> +     return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
>> +                               rdev->desc->enable_mask, AXP20X_IO_ENABLED);
>> +}
>
> Please make some helpers for this (or extend the existing ones to cope
> with more than a single bit control) - there's several other devices
> which have similar multi-bit controls so we should factor this out
> rather than open coding.

No prob, I'll submit a patch for the helpers before submitting the v2
of this patchset

>> +     np = of_node_get(pdev->dev.parent->of_node);
>> +     if (!np)
>> +             return 0;
>> +
>> +     regulators = of_find_node_by_name(np, "regulators");
>> +     if (!regulators) {
>> +             dev_err(&pdev->dev, "regulators node not found\n");
>> +             return -EINVAL;
>> +     }
>
> The driver should be able to start up with no configuration provided at
> all except for the device being registered - the user won't be able to
> change anything but they will be able to read the current state of the
> hardware which can be useful for diagnostics.

seems reasonable

>> +static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 workmode)
>> +{
>> +     unsigned int mask = AXP20X_WORKMODE_DCDC2_MASK;
>> +
>> +     if ((id != AXP20X_DCDC2) && (id != AXP20X_DCDC3))
>> +             return -EINVAL;
>> +
>> +     if (id == AXP20X_DCDC3)
>> +             mask = AXP20X_WORKMODE_DCDC3_MASK;
>> +
>> +     return regmap_update_bits(rdev->regmap, AXP20X_DCDC_MODE, mask, workmode);
>> +}
>
> What is a workmode?

It defines if the output from DCDC2 or DCDC3 (that can be used as PWM chargers).
I'll document it better in the bindings documentation.

>> +             pmic->rdev[i] = regulator_register(&pmic->rdesc[i], &config);
>> +             if (IS_ERR(pmic->rdev[i])) {
>
> Use devm_regulator_register() and then you don't need to clean up the
> regulators either.
>
>> +static int __init axp20x_regulator_init(void)
>> +{
>> +     return platform_driver_register(&axp20x_regulator_driver);
>> +}
>> +
>> +subsys_initcall(axp20x_regulator_init);
>
> module_platform_driver().

I'll fix both in v2.

Thank you,

-- 
Carlo Caione

^ permalink raw reply

* Re: [PATCH 1/4] Input: wacom: Use full 32-bit HID Usage value in switch statement
From: Ping Cheng @ 2014-03-04 21:10 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, Carl Worth, Aaron Skomra

Hi Dmitry,

This patchset has been Tested-by: Aaron Skomra
<Aaron.Skomra@wacom.com> and Reviewed-by: Carl Worth
<cworth@cworth.org>. I think they are ready to go upstream.

Do you have other comments?

Ping

On Thu, Feb 27, 2014 at 10:37 AM, Aaron Armstrong Skomra
<skomra@gmail.com> wrote:
> On Thu, Jan 30, 2014 at 10:48 AM, Jason Gerecke <killertofu@gmail.com> wrote:
>>
>> A HID Usage is a 32-bit value: an upper 16-bit "page" and a lower
>> 16-bit ID. While the two halves are normally reported seperately,
>> only the combination uniquely idenfifes a particular HID Usage.
>>
>> The existing code performs the comparison in two steps, first
>> performing a switch on the ID and then verifying the page within
>> each case. While this works fine, it is very akward to handle
>> two Usages that share a single ID, such as HID_USAGE_PRESSURE
>> and HID_USAGE_X because the case statement can only have a
>> single identifier.
>>
>> To work around this, we now check the full 32-bit HID Usage
>> directly rather than first checking the ID and then the page.
>> This allows the switch statement to have distinct cases for
>> e.g. HID_USAGE_PRESSURE and HID_USAGE_X.
>>
>> Signed-off-by: Jason Gerecke <killertofu@gmail.com>
>> ---
>>  drivers/input/tablet/wacom_sys.c | 237 ++++++++++++++++++---------------------
>>  1 file changed, 109 insertions(+), 128 deletions(-)
>>
>> diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
>> index b16ebef..5be6177 100644
>> --- a/drivers/input/tablet/wacom_sys.c
>> +++ b/drivers/input/tablet/wacom_sys.c
>> @@ -22,23 +22,17 @@
>>  #define HID_USAGE_PAGE_DIGITIZER       0x0d
>>  #define HID_USAGE_PAGE_DESKTOP         0x01
>>  #define HID_USAGE                      0x09
>> -#define HID_USAGE_X                    0x30
>> -#define HID_USAGE_Y                    0x31
>> -#define HID_USAGE_X_TILT               0x3d
>> -#define HID_USAGE_Y_TILT               0x3e
>> -#define HID_USAGE_FINGER               0x22
>> -#define HID_USAGE_STYLUS               0x20
>> -#define HID_USAGE_CONTACTMAX           0x55
>> +#define HID_USAGE_X                    ((HID_USAGE_PAGE_DESKTOP << 16) | 0x30)
>> +#define HID_USAGE_Y                    ((HID_USAGE_PAGE_DESKTOP << 16) | 0x31)
>> +#define HID_USAGE_X_TILT               ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x3d)
>> +#define HID_USAGE_Y_TILT               ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x3e)
>> +#define HID_USAGE_FINGER               ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x22)
>> +#define HID_USAGE_STYLUS               ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x20)
>> +#define HID_USAGE_CONTACTMAX           ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x55)
>>  #define HID_COLLECTION                 0xa1
>>  #define HID_COLLECTION_LOGICAL         0x02
>>  #define HID_COLLECTION_END             0xc0
>>
>> -enum {
>> -       WCM_UNDEFINED = 0,
>> -       WCM_DESKTOP,
>> -       WCM_DIGITIZER,
>> -};
>> -
>>  struct hid_descriptor {
>>         struct usb_descriptor_header header;
>>         __le16   bcdHID;
>> @@ -305,7 +299,7 @@ static int wacom_parse_hid(struct usb_interface *intf,
>>         char limit = 0;
>>         /* result has to be defined as int for some devices */
>>         int result = 0, touch_max = 0;
>> -       int i = 0, usage = WCM_UNDEFINED, finger = 0, pen = 0;
>> +       int i = 0, page = 0, finger = 0, pen = 0;
>>         unsigned char *report;
>>
>>         report = kzalloc(hid_desc->wDescriptorLength, GFP_KERNEL);
>> @@ -332,134 +326,121 @@ static int wacom_parse_hid(struct usb_interface *intf,
>>
>>                 switch (report[i]) {
>>                 case HID_USAGE_PAGE:
>> -                       switch (report[i + 1]) {
>> -                       case HID_USAGE_PAGE_DIGITIZER:
>> -                               usage = WCM_DIGITIZER;
>> -                               i++;
>> -                               break;
>> -
>> -                       case HID_USAGE_PAGE_DESKTOP:
>> -                               usage = WCM_DESKTOP;
>> -                               i++;
>> -                               break;
>> -                       }
>> +                       page = report[i + 1];
>> +                       i++;
>>                         break;
>>
>>                 case HID_USAGE:
>> -                       switch (report[i + 1]) {
>> +                       switch (page << 16 | report[i + 1]) {
>>                         case HID_USAGE_X:
>> -                               if (usage == WCM_DESKTOP) {
>> -                                       if (finger) {
>> -                                               features->device_type = BTN_TOOL_FINGER;
>> -                                               /* touch device at least supports one touch point */
>> -                                               touch_max = 1;
>> -                                               switch (features->type) {
>> -                                               case TABLETPC2FG:
>> -                                                       features->pktlen = WACOM_PKGLEN_TPC2FG;
>> -                                                       break;
>> -
>> -                                               case MTSCREEN:
>> -                                               case WACOM_24HDT:
>> -                                                       features->pktlen = WACOM_PKGLEN_MTOUCH;
>> -                                                       break;
>> -
>> -                                               case MTTPC:
>> -                                                       features->pktlen = WACOM_PKGLEN_MTTPC;
>> -                                                       break;
>> -
>> -                                               case BAMBOO_PT:
>> -                                                       features->pktlen = WACOM_PKGLEN_BBTOUCH;
>> -                                                       break;
>> -
>> -                                               default:
>> -                                                       features->pktlen = WACOM_PKGLEN_GRAPHIRE;
>> -                                                       break;
>> -                                               }
>> -
>> -                                               switch (features->type) {
>> -                                               case BAMBOO_PT:
>> -                                                       features->x_phy =
>> -                                                               get_unaligned_le16(&report[i + 5]);
>> -                                                       features->x_max =
>> -                                                               get_unaligned_le16(&report[i + 8]);
>> -                                                       i += 15;
>> -                                                       break;
>> -
>> -                                               case WACOM_24HDT:
>> -                                                       features->x_max =
>> -                                                               get_unaligned_le16(&report[i + 3]);
>> -                                                       features->x_phy =
>> -                                                               get_unaligned_le16(&report[i + 8]);
>> -                                                       features->unit = report[i - 1];
>> -                                                       features->unitExpo = report[i - 3];
>> -                                                       i += 12;
>> -                                                       break;
>> -
>> -                                               default:
>> -                                                       features->x_max =
>> -                                                               get_unaligned_le16(&report[i + 3]);
>> -                                                       features->x_phy =
>> -                                                               get_unaligned_le16(&report[i + 6]);
>> -                                                       features->unit = report[i + 9];
>> -                                                       features->unitExpo = report[i + 11];
>> -                                                       i += 12;
>> -                                                       break;
>> -                                               }
>> -                                       } else if (pen) {
>> -                                               /* penabled only accepts exact bytes of data */
>> -                                               if (features->type >= TABLETPC)
>> -                                                       features->pktlen = WACOM_PKGLEN_GRAPHIRE;
>> -                                               features->device_type = BTN_TOOL_PEN;
>> +                               if (finger) {
>> +                                       features->device_type = BTN_TOOL_FINGER;
>> +                                       /* touch device at least supports one touch point */
>> +                                       touch_max = 1;
>> +                                       switch (features->type) {
>> +                                       case TABLETPC2FG:
>> +                                               features->pktlen = WACOM_PKGLEN_TPC2FG;
>> +                                               break;
>> +
>> +                                       case MTSCREEN:
>> +                                       case WACOM_24HDT:
>> +                                               features->pktlen = WACOM_PKGLEN_MTOUCH;
>> +                                               break;
>> +
>> +                                       case MTTPC:
>> +                                               features->pktlen = WACOM_PKGLEN_MTTPC;
>> +                                               break;
>> +
>> +                                       case BAMBOO_PT:
>> +                                               features->pktlen = WACOM_PKGLEN_BBTOUCH;
>> +                                               break;
>> +
>> +                                       default:
>> +                                               features->pktlen = WACOM_PKGLEN_GRAPHIRE;
>> +                                               break;
>> +                                       }
>> +
>> +                                       switch (features->type) {
>> +                                       case BAMBOO_PT:
>> +                                               features->x_phy =
>> +                                                       get_unaligned_le16(&report[i + 5]);
>> +                                               features->x_max =
>> +                                                       get_unaligned_le16(&report[i + 8]);
>> +                                               i += 15;
>> +                                               break;
>> +
>> +                                       case WACOM_24HDT:
>>                                                 features->x_max =
>>                                                         get_unaligned_le16(&report[i + 3]);
>> -                                               i += 4;
>> +                                               features->x_phy =
>> +                                                       get_unaligned_le16(&report[i + 8]);
>> +                                               features->unit = report[i - 1];
>> +                                               features->unitExpo = report[i - 3];
>> +                                               i += 12;
>> +                                               break;
>> +
>> +                                       default:
>> +                                               features->x_max =
>> +                                                       get_unaligned_le16(&report[i + 3]);
>> +                                               features->x_phy =
>> +                                                       get_unaligned_le16(&report[i + 6]);
>> +                                               features->unit = report[i + 9];
>> +                                               features->unitExpo = report[i + 11];
>> +                                               i += 12;
>> +                                               break;
>>                                         }
>> +                               } else if (pen) {
>> +                                       /* penabled only accepts exact bytes of data */
>> +                                       if (features->type >= TABLETPC)
>> +                                               features->pktlen = WACOM_PKGLEN_GRAPHIRE;
>> +                                       features->device_type = BTN_TOOL_PEN;
>> +                                       features->x_max =
>> +                                               get_unaligned_le16(&report[i + 3]);
>> +                                       i += 4;
>>                                 }
>>                                 break;
>>
>>                         case HID_USAGE_Y:
>> -                               if (usage == WCM_DESKTOP) {
>> -                                       if (finger) {
>> -                                               switch (features->type) {
>> -                                               case TABLETPC2FG:
>> -                                               case MTSCREEN:
>> -                                               case MTTPC:
>> -                                                       features->y_max =
>> -                                                               get_unaligned_le16(&report[i + 3]);
>> -                                                       features->y_phy =
>> -                                                               get_unaligned_le16(&report[i + 6]);
>> -                                                       i += 7;
>> -                                                       break;
>> -
>> -                                               case WACOM_24HDT:
>> -                                                       features->y_max =
>> -                                                               get_unaligned_le16(&report[i + 3]);
>> -                                                       features->y_phy =
>> -                                                               get_unaligned_le16(&report[i - 2]);
>> -                                                       i += 7;
>> -                                                       break;
>> -
>> -                                               case BAMBOO_PT:
>> -                                                       features->y_phy =
>> -                                                               get_unaligned_le16(&report[i + 3]);
>> -                                                       features->y_max =
>> -                                                               get_unaligned_le16(&report[i + 6]);
>> -                                                       i += 12;
>> -                                                       break;
>> -
>> -                                               default:
>> -                                                       features->y_max =
>> -                                                               features->x_max;
>> -                                                       features->y_phy =
>> -                                                               get_unaligned_le16(&report[i + 3]);
>> -                                                       i += 4;
>> -                                                       break;
>> -                                               }
>> -                                       } else if (pen) {
>> +                               if (finger) {
>> +                                       switch (features->type) {
>> +                                       case TABLETPC2FG:
>> +                                       case MTSCREEN:
>> +                                       case MTTPC:
>> +                                               features->y_max =
>> +                                                       get_unaligned_le16(&report[i + 3]);
>> +                                               features->y_phy =
>> +                                                       get_unaligned_le16(&report[i + 6]);
>> +                                               i += 7;
>> +                                               break;
>> +
>> +                                       case WACOM_24HDT:
>> +                                               features->y_max =
>> +                                                       get_unaligned_le16(&report[i + 3]);
>> +                                               features->y_phy =
>> +                                                       get_unaligned_le16(&report[i - 2]);
>> +                                               i += 7;
>> +                                               break;
>> +
>> +                                       case BAMBOO_PT:
>> +                                               features->y_phy =
>> +                                                       get_unaligned_le16(&report[i + 3]);
>> +                                               features->y_max =
>> +                                                       get_unaligned_le16(&report[i + 6]);
>> +                                               i += 12;
>> +                                               break;
>> +
>> +                                       default:
>>                                                 features->y_max =
>> +                                                       features->x_max;
>> +                                               features->y_phy =
>>                                                         get_unaligned_le16(&report[i + 3]);
>>                                                 i += 4;
>> +                                               break;
>>                                         }
>> +                               } else if (pen) {
>> +                                       features->y_max =
>> +                                               get_unaligned_le16(&report[i + 3]);
>> +                                       i += 4;
>>                                 }
>>                                 break;
>>
>> @@ -489,7 +470,7 @@ static int wacom_parse_hid(struct usb_interface *intf,
>>
>>                 case HID_COLLECTION_END:
>>                         /* reset UsagePage and Finger */
>> -                       finger = usage = 0;
>> +                       finger = page = 0;
>>                         break;
>>
>>                 case HID_COLLECTION:
>> --
>> 1.8.5.3
>>
> Tested-by: Aaron Skomra <Aaron.Skomra@wacom.com>
> --
> 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

* USB PCT multitouch 0eef:7200 (eGalaxTouch EXC7200-766Dv1.000)  PATCH INCLUDED
From: Jan Kandziora @ 2014-03-04 21:58 UTC (permalink / raw)
  To: linux-input

Hi,

I found this touchscreen chip built into the board of the Posiflex
XT3015 cash register hardware.


Sadly, it doesn't work with the current usbhid OOTB. As the device ID
isn't in the hid-ids.h, I tried adding it:

--------------------------------------------------------------------------
diff -urN linux-3.10.27.orig/drivers/hid/hid-ids.h
linux-3.10.27/drivers/hid/hid-ids.h
--- linux-3.10.27.orig/drivers/hid/hid-ids.h    2014-01-16
00:29:14.000000000 +0100
+++ linux-3.10.27/drivers/hid/hid-ids.h 2014-03-04 20:51:09.889687222 +0100
@@ -268,6 +268,7 @@
 #define USB_DEVICE_ID_DWAV_TOUCHCONTROLLER     0x0002
 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D      0x480d
 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E      0x480e
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7200      0x7200
 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7207      0x7207
 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C      0x720c
 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224      0x7224
diff -urN linux-3.10.27.orig/drivers/hid/hid-multitouch.c
linux-3.10.27/drivers/hid/hid-multitouch.c
--- linux-3.10.27.orig/drivers/hid/hid-multitouch.c     2014-01-16
00:29:14.000000000 +0100
+++ linux-3.10.27/drivers/hid/hid-multitouch.c  2014-03-04
20:48:35.351350357 +0100
@@ -1166,6 +1166,9 @@
                        USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) },
        { .driver_data = MT_CLS_EGALAX,
                HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
+                       USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7200) },
+       { .driver_data = MT_CLS_EGALAX,
+               HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
                        USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224) },
        { .driver_data = MT_CLS_EGALAX,
                HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
--------------------------------------------------------------------------

That works. Haven't checked the multitouch functions, though.


Kind regards

	Jan

^ permalink raw reply

* [PATCH v2] input synaptics-rmi4: Add F30 support
From: Christopher Heiny @ 2014-03-04 22:52 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
	Vivian Ly, Daniel Rosenberg, Linus Walleij, Allie Xiong,
	Benjamin Tissoires, David Herrmann, Jiri Kosina

RMI4 Function 0x30 provides support for GPIOs, LEDs and mechanical
buttons.  In particular, the mechanical button support is used in
an increasing number of touchpads.

Note - this is basically the same as last week's patch, just updated to
work with the changes committed to synaptics-rmi4  branch over the weekend.

Signed-off-by: Andrew Duggan <aduggan@synaptics.com>
Signed-off-by: Allie Xiong <axiong@synaptics.com>
Acked-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Linux Walleij <linus.walleij@linaro.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>

---

 drivers/input/rmi4/Kconfig   |  12 +
 drivers/input/rmi4/Makefile  |   1 +
 drivers/input/rmi4/rmi_f30.c | 618 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/rmi.h          |   7 +-
 4 files changed, 637 insertions(+), 1 deletion(-)

diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
index d0c7b6e..14d5f49 100644
--- a/drivers/input/rmi4/Kconfig
+++ b/drivers/input/rmi4/Kconfig
@@ -63,3 +63,15 @@ config RMI4_F11_PEN
 	  If your system is not recognizing pen touches and you know your
 	  sensor supports pen input, you probably want to turn this feature
 	  off.
+
+config RMI4_F30
+        tristate "RMI4 Function 30 (GPIO LED)"
+        depends on RMI4_CORE
+        help
+          Say Y here if you want to add support for RMI4 function 30.
+
+          Function 30 provides GPIO and LED support for RMI4 devices. This
+	  includes support for buttons on TouchPads and ClickPads.
+
+          To compile this driver as a module, choose M here: the
+          module will be called rmi-f30.
diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile
index 5c6bad5..ecffd72 100644
--- a/drivers/input/rmi4/Makefile
+++ b/drivers/input/rmi4/Makefile
@@ -3,6 +3,7 @@ rmi_core-y := rmi_bus.o rmi_driver.o rmi_f01.o
 
 # Function drivers
 obj-$(CONFIG_RMI4_F11) += rmi_f11.o
+obj-$(CONFIG_RMI4_F30) += rmi_f30.o
 
 # Transports
 obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o
diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c
new file mode 100644
index 0000000..558b3a3
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f30.c
@@ -0,0 +1,618 @@
+/*
+ * Copyright (c) 2012 - 2014 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/leds.h>
+#include "rmi_driver.h"
+
+#define RMI_F30_QUERY_SIZE			2
+
+ /* Defs for Query 0 */
+#define RMI_F30_EXTENDED_PATTERNS		0x01
+#define RMI_F30_HAS_MAPPABLE_BUTTONS		(1 << 1)
+#define RMI_F30_HAS_LED				(1 << 2)
+#define RMI_F30_HAS_GPIO			(1 << 3)
+#define RMI_F30_HAS_HAPTIC			(1 << 4)
+#define RMI_F30_HAS_GPIO_DRV_CTL		(1 << 5)
+#define RMI_F30_HAS_MECH_MOUSE_BTNS		(1 << 6)
+
+/* Defs for Query 1 */
+#define RMI_F30_GPIO_LED_COUNT			0x1F
+
+/* Defs for Control Registers */
+#define RMI_F30_CTRL_1_GPIO_DEBOUNCE		0x01
+#define RMI_F30_CTRL_1_HALT			(1 << 4)
+#define RMI_F30_CTRL_1_HALTED			(1 << 5)
+#define RMI_F30_CTRL_10_NUM_MECH_MOUSE_BTNS	0x03
+
+#define RMI_F30_CTRL_DATA(ctrl_num, max_size) \
+struct rmi_f30_ctrl##ctrl_num##_data { \
+	int address; \
+	int length; \
+	u8 regs[max_size]; \
+}
+
+#define RMI_F30_CTRL_MAX_REGS	32
+#define RMI_F30_CTRL_MAX_BYTES	((RMI_F30_CTRL_MAX_REGS + 7) >> 3)
+
+struct f30_data {
+	/* Query Data */
+	bool has_extended_pattern;
+	bool has_mappable_buttons;
+	bool has_led;
+	bool has_gpio;
+	bool has_haptic;
+	bool has_gpio_driver_control;
+	bool has_mech_mouse_btns;
+	u8 gpioled_count;
+
+	u8 register_count;
+
+	/* Control Register Data */
+	RMI_F30_CTRL_DATA(0, RMI_F30_CTRL_MAX_BYTES)	ctrl_0;
+	RMI_F30_CTRL_DATA(1, 1)				ctrl_1;
+	RMI_F30_CTRL_DATA(2, RMI_F30_CTRL_MAX_BYTES)	ctrl_2;
+	RMI_F30_CTRL_DATA(3, RMI_F30_CTRL_MAX_BYTES)	ctrl_3;
+	RMI_F30_CTRL_DATA(4, RMI_F30_CTRL_MAX_BYTES)	ctrl_4;
+	RMI_F30_CTRL_DATA(5, 6)				ctrl_5;
+	RMI_F30_CTRL_DATA(6, RMI_F30_CTRL_MAX_REGS)	ctrl_6;
+	RMI_F30_CTRL_DATA(7, RMI_F30_CTRL_MAX_REGS)	ctrl_7;
+	RMI_F30_CTRL_DATA(8, RMI_F30_CTRL_MAX_BYTES)	ctrl_8;
+	RMI_F30_CTRL_DATA(9, 1)				ctrl_9;
+	RMI_F30_CTRL_DATA(10, 1)			ctrl_10;
+
+	u8 data_regs[RMI_F30_CTRL_MAX_BYTES];
+	struct rmi_f30_button *gpioled_map;
+
+	char input_phys[NAME_BUFFER_SIZE];
+	struct input_dev *input;
+};
+
+static int rmi_f30_read_control_parameters(struct rmi_device *rmi_dev,
+	struct f30_data *f30)
+{
+	int retval = 0;
+
+	if (f30->ctrl_0.length) {
+		retval = rmi_read_block(rmi_dev, f30->ctrl_0.address,
+				f30->ctrl_0.regs, f30->ctrl_0.length);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev,
+				"%s : Could not read control reg0 to 0x%x\n",
+				__func__, f30->ctrl_0.address);
+			return retval;
+		}
+	}
+
+	if (f30->ctrl_1.length) {
+		retval = rmi_read_block(rmi_dev, f30->ctrl_1.address,
+				f30->ctrl_1.regs, f30->ctrl_1.length);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev,
+				"%s : Could not read control reg1 to 0x%x\n",
+				 __func__, f30->ctrl_1.address);
+			return retval;
+		}
+	}
+
+	if (f30->ctrl_2.length) {
+		retval = rmi_read_block(rmi_dev, f30->ctrl_2.address,
+				f30->ctrl_2.regs, f30->ctrl_2.length);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev,
+				"%s : Could not read control reg2 to 0x%x\n",
+				 __func__, f30->ctrl_2.address);
+			return retval;
+		}
+	}
+
+	if (f30->ctrl_3.length) {
+		retval = rmi_read_block(rmi_dev, f30->ctrl_3.address,
+				f30->ctrl_3.regs, f30->ctrl_3.length);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev,
+				"%s : Could not read control reg3 to 0x%x\n",
+				 __func__, f30->ctrl_3.address);
+			return retval;
+		}
+	}
+
+	if (f30->ctrl_4.length) {
+		retval = rmi_read_block(rmi_dev, f30->ctrl_4.address,
+				f30->ctrl_4.regs, f30->ctrl_4.length);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev,
+				"%s : Could not read control reg4 to 0x%x\n",
+				 __func__, f30->ctrl_4.address);
+			return retval;
+		}
+	}
+
+	if (f30->ctrl_5.length) {
+		retval = rmi_read_block(rmi_dev, f30->ctrl_5.address,
+				f30->ctrl_5.regs, f30->ctrl_5.length);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev,
+				"%s : Could not read control reg5 to 0x%x\n",
+				 __func__, f30->ctrl_5.address);
+			return retval;
+		}
+	}
+
+	if (f30->ctrl_6.length) {
+		retval = rmi_read_block(rmi_dev, f30->ctrl_6.address,
+				f30->ctrl_6.regs, f30->ctrl_6.length);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev,
+				"%s : Could not read control reg6 to 0x%x\n",
+				 __func__, f30->ctrl_6.address);
+			return retval;
+		}
+	}
+
+	if (f30->ctrl_7.length) {
+		retval = rmi_read_block(rmi_dev, f30->ctrl_7.address,
+				f30->ctrl_1.regs, f30->ctrl_7.length);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev,
+				"%s : Could not read control reg7 to 0x%x\n",
+				 __func__, f30->ctrl_7.address);
+			return retval;
+		}
+	}
+
+	if (f30->ctrl_8.length) {
+		retval = rmi_read_block(rmi_dev, f30->ctrl_8.address,
+				f30->ctrl_8.regs, f30->ctrl_8.length);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev,
+				"%s : Could not read control reg8 to 0x%x\n",
+				 __func__, f30->ctrl_8.address);
+			return retval;
+		}
+	}
+
+	if (f30->ctrl_9.length) {
+		retval = rmi_read_block(rmi_dev, f30->ctrl_9.address,
+				f30->ctrl_9.regs, f30->ctrl_9.length);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev,
+				"%s : Could not read control reg9 to 0x%x\n",
+				 __func__, f30->ctrl_9.address);
+			return retval;
+		}
+	}
+
+	if (f30->ctrl_10.length) {
+		retval = rmi_read_block(rmi_dev, f30->ctrl_10.address,
+				f30->ctrl_10.regs, f30->ctrl_10.length);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev,
+				"%s : Could not read control reg1 to 0x%x\n",
+				 __func__, f30->ctrl_10.address);
+			return retval;
+		}
+	}
+
+	return 0;
+}
+
+static int rmi_f30_attention(struct rmi_function *fn,
+					unsigned long *irq_bits)
+{
+	struct f30_data *f30 = dev_get_drvdata(&fn->dev);
+	int retval;
+	int gpiled = 0;
+	int value = 0;
+	int i;
+	int reg_num;
+
+	/* Read the gpi led data. */
+	retval = rmi_read_block(fn->rmi_dev, fn->fd.data_base_addr,
+		f30->data_regs, f30->register_count);
+
+	if (retval < 0) {
+		dev_err(&fn->dev, "%s: Failed to read F30 data registers.\n",
+			__func__);
+		return retval;
+	}
+
+	for (reg_num = 0; reg_num < f30->register_count; ++reg_num) {
+		for (i = 0; gpiled < f30->gpioled_count && i < 8; ++i,
+			++gpiled) {
+			if (f30->gpioled_map[gpiled].button != 0) {
+				value = (((f30->data_regs[reg_num] >> i) & 0x01)
+					 == f30->gpioled_map[gpiled].sense);
+
+				dev_dbg(&fn->dev,
+					"%s: call input report key (0x%04x) value (0x%02x)",
+					__func__,
+					f30->gpioled_map[gpiled].button, value);
+				input_report_key(f30->input,
+					f30->gpioled_map[gpiled].button,
+					value);
+			}
+
+		}
+	}
+
+	input_sync(f30->input); /* sync after groups of events */
+	return 0;
+}
+
+static int rmi_f30_register_device(struct rmi_function *fn)
+{
+	int i;
+	int rc;
+	struct rmi_device *rmi_dev = fn->rmi_dev;
+	struct f30_data *f30 = dev_get_drvdata(&fn->dev);
+	struct rmi_driver *driver = fn->rmi_dev->driver;
+	struct input_dev *input_dev = input_allocate_device();
+
+	if (!input_dev) {
+		dev_err(&fn->dev, "Failed to allocate input device.\n");
+		return -ENOMEM;
+	}
+
+	f30->input = input_dev;
+
+	if (driver->set_input_params) {
+		rc = driver->set_input_params(rmi_dev, input_dev);
+		if (rc < 0) {
+			dev_err(&fn->dev, "%s: Error in setting input device.\n",
+				__func__);
+			goto error_free_device;
+		}
+	}
+	sprintf(f30->input_phys, "%s/input0", dev_name(&fn->dev));
+	input_dev->phys = f30->input_phys;
+	input_dev->dev.parent = &rmi_dev->dev;
+	input_set_drvdata(input_dev, f30);
+
+	/* Set up any input events. */
+	set_bit(EV_SYN, input_dev->evbit);
+	set_bit(EV_KEY, input_dev->evbit);
+	input_dev->keycode = f30->gpioled_map;
+	input_dev->keycodesize = 1;
+	input_dev->keycodemax = f30->gpioled_count;
+	/* set bits for each qpio led pin... */
+	for (i = 0; i < f30->gpioled_count; i++) {
+		if (f30->gpioled_map[i].button != 0) {
+			set_bit(f30->gpioled_map[i].button, input_dev->keybit);
+			input_set_capability(input_dev, EV_KEY,
+						f30->gpioled_map[i].button);
+		}
+	}
+
+	rc = input_register_device(input_dev);
+	if (rc < 0) {
+		dev_err(&fn->dev, "Failed to register input device.\n");
+		goto error_free_device;
+	}
+	return 0;
+
+error_free_device:
+	input_free_device(input_dev);
+
+	return rc;
+}
+
+static int rmi_f30_config(struct rmi_function *fn)
+{
+	struct f30_data *f30 = dev_get_drvdata(&fn->dev);
+	int rc;
+
+	/* Write Control Register values back to device */
+	if (f30->ctrl_0.length) {
+		rc = rmi_write_block(fn->rmi_dev, f30->ctrl_0.address,
+					f30->ctrl_0.regs,
+					f30->ctrl_0.length);
+		if (rc < 0) {
+			dev_err(&fn->dev, "%s error %d: Could not write control 0 to 0x%x\n",
+				__func__, rc, f30->ctrl_0.address);
+			return rc;
+		}
+	}
+
+	if (f30->ctrl_1.length) {
+		rc = rmi_write_block(fn->rmi_dev, f30->ctrl_1.address,
+					f30->ctrl_1.regs,
+					f30->ctrl_1.length);
+		if (rc < 0) {
+			dev_err(&fn->dev, "%s error %d: Could not write control 1 to 0x%x\n",
+					__func__, rc, f30->ctrl_1.address);
+			return rc;
+		}
+	}
+
+	if (f30->ctrl_2.length) {
+		rc = rmi_write_block(fn->rmi_dev, f30->ctrl_2.address,
+					f30->ctrl_2.regs,
+					f30->ctrl_2.length);
+		if (rc < 0) {
+			dev_err(&fn->dev, "%s error %d: Could not write control 2 to 0x%x\n",
+					__func__, rc, f30->ctrl_2.address);
+			return rc;
+		}
+	}
+
+	if (f30->ctrl_3.length) {
+		rc = rmi_write_block(fn->rmi_dev, f30->ctrl_3.address,
+					f30->ctrl_3.regs,
+					f30->ctrl_3.length);
+		if (rc < 0) {
+			dev_err(&fn->dev, "%s error %d: Could not write control 3 to 0x%x\n",
+				__func__, rc, f30->ctrl_3.address);
+			return rc;
+		}
+	}
+
+	if (f30->ctrl_4.length) {
+		rc = rmi_write_block(fn->rmi_dev, f30->ctrl_4.address,
+					f30->ctrl_4.regs,
+					f30->ctrl_4.length);
+		if (rc < 0) {
+			dev_err(&fn->dev, "%s error %d: Could not write control 4 to 0x%x\n",
+				__func__, rc, f30->ctrl_4.address);
+			return rc;
+		}
+	}
+
+	if (f30->ctrl_5.length) {
+		rc = rmi_write_block(fn->rmi_dev, f30->ctrl_5.address,
+					f30->ctrl_5.regs,
+					f30->ctrl_5.length);
+		if (rc < 0) {
+			dev_err(&fn->dev, "%s error %d: Could not write control 5 to 0x%x\n",
+				__func__, rc, f30->ctrl_5.address);
+			return rc;
+		}
+	}
+
+	if (f30->ctrl_6.length) {
+		rc = rmi_write_block(fn->rmi_dev, f30->ctrl_6.address,
+					f30->ctrl_6.regs,
+					f30->ctrl_6.length);
+		if (rc < 0) {
+			dev_err(&fn->dev, "%s error %d: Could not write control 6 to 0x%x\n",
+				__func__, rc, f30->ctrl_6.address);
+			return rc;
+		}
+	}
+
+	if (f30->ctrl_7.length) {
+		rc = rmi_write_block(fn->rmi_dev, f30->ctrl_7.address,
+					f30->ctrl_7.regs,
+					f30->ctrl_7.length);
+		if (rc < 0) {
+			dev_err(&fn->dev, "%s error %d: Could not write control 7 to 0x%x\n",
+				__func__, rc, f30->ctrl_7.address);
+			return rc;
+		}
+	}
+
+	if (f30->ctrl_8.length) {
+		rc = rmi_write_block(fn->rmi_dev, f30->ctrl_8.address,
+					f30->ctrl_8.regs,
+					f30->ctrl_8.length);
+		if (rc < 0) {
+			dev_err(&fn->dev, "%s error %d: Could not write control 8 to 0x%x\n",
+				__func__, rc, f30->ctrl_8.address);
+			return rc;
+		}
+	}
+
+	if (f30->ctrl_9.length) {
+		rc = rmi_write_block(fn->rmi_dev, f30->ctrl_9.address,
+					f30->ctrl_9.regs,
+					f30->ctrl_9.length);
+		if (rc < 0) {
+			dev_err(&fn->dev, "%s error %d: Could not write control 9 to 0x%x\n",
+				__func__, rc, f30->ctrl_9.address);
+			return rc;
+		}
+	}
+
+	if (f30->ctrl_10.length) {
+		rc = rmi_write_block(fn->rmi_dev, f30->ctrl_10.address,
+					f30->ctrl_10.regs,
+					f30->ctrl_10.length);
+		if (rc < 0) {
+			dev_err(&fn->dev, "%s error %d: Could not write control 10 to 0x%x\n",
+				__func__, rc, f30->ctrl_10.address);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static inline int rmi_f30_initialize(struct rmi_function *fn)
+{
+	struct f30_data *f30;
+	struct rmi_device *rmi_dev = fn->rmi_dev;
+	const struct rmi_device_platform_data *pdata;
+	int retval = 0;
+	int control_address;
+	u8 buf[RMI_F30_QUERY_SIZE];
+
+	f30 = devm_kzalloc(&fn->dev, sizeof(struct f30_data),
+			   GFP_KERNEL);
+	if (!f30) {
+		dev_err(&fn->dev, "Failed to allocate f30_data.\n");
+		return -ENOMEM;
+	}
+	dev_set_drvdata(&fn->dev, f30);
+
+	retval = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr, buf,
+				RMI_F30_QUERY_SIZE);
+
+	if (retval < 0) {
+		dev_err(&fn->dev, "Failed to read query register.\n");
+		return retval;
+	}
+
+	f30->has_extended_pattern = buf[0] & RMI_F30_EXTENDED_PATTERNS;
+	f30->has_mappable_buttons = buf[0] & RMI_F30_HAS_MAPPABLE_BUTTONS;
+	f30->has_led = buf[0] & RMI_F30_HAS_LED;
+	f30->has_gpio = buf[0] & RMI_F30_HAS_GPIO;
+	f30->has_haptic = buf[0] & RMI_F30_HAS_HAPTIC;
+	f30->has_gpio_driver_control = buf[0] & RMI_F30_HAS_GPIO_DRV_CTL;
+	f30->has_mech_mouse_btns = buf[0] & RMI_F30_HAS_MECH_MOUSE_BTNS;
+	f30->gpioled_count = buf[1] & RMI_F30_GPIO_LED_COUNT;
+
+	f30->register_count = (f30->gpioled_count + 7) >> 3;
+
+	control_address = fn->fd.control_base_addr;
+
+	/* Allocate buffers for the control registers */
+	if (f30->has_led && f30->has_led) {
+		f30->ctrl_0.address = control_address;
+		f30->ctrl_0.length = f30->register_count;
+		control_address += f30->ctrl_0.length;
+	}
+
+	f30->ctrl_1.address = control_address;
+	f30->ctrl_1.length = sizeof(u8);
+	control_address += f30->ctrl_1.length;
+
+	if (f30->has_gpio) {
+		f30->ctrl_2.address = control_address;
+		f30->ctrl_2.length = f30->register_count;
+		control_address += f30->ctrl_2.length;
+
+		f30->ctrl_3.address = control_address;
+		f30->ctrl_3.length = f30->register_count;
+		control_address += f30->ctrl_3.length;
+	}
+
+	if (f30->has_led) {
+		f30->ctrl_4.address = control_address;
+		f30->ctrl_4.length = f30->register_count;
+		control_address += f30->ctrl_4.length;
+
+		if (f30->has_extended_pattern)
+			f30->ctrl_5.length = 6;
+		else
+			f30->ctrl_5.length = 2;
+
+		f30->ctrl_5.address = control_address;
+		control_address += f30->ctrl_5.length;
+	}
+
+	if (f30->has_led || f30->has_gpio_driver_control) {
+		/* control 6 uses a byte per gpio/led */
+		f30->ctrl_6.address = control_address;
+		f30->ctrl_6.length = f30->gpioled_count;
+		control_address += f30->ctrl_6.length;
+
+	}
+
+	if (f30->has_mappable_buttons) {
+		/* control 7 uses a byte per gpio/led */
+		f30->ctrl_7.address = control_address;
+		f30->ctrl_7.length = f30->gpioled_count;
+		control_address += f30->ctrl_7.length;
+	}
+
+	if (f30->has_haptic) {
+		f30->ctrl_8.address = control_address;
+		f30->ctrl_8.length = f30->register_count;
+		control_address += f30->ctrl_8.length;
+
+		f30->ctrl_9.address = control_address;
+		f30->ctrl_9.length = sizeof(u8);
+		control_address += f30->ctrl_9.length;
+	}
+
+	if (f30->has_mech_mouse_btns) {
+		f30->ctrl_10.address = control_address;
+		f30->ctrl_10.length = sizeof(u8);
+		control_address += f30->ctrl_10.length;
+	}
+
+	f30->gpioled_map = devm_kzalloc(&fn->dev,
+				f30->gpioled_count
+				* sizeof(struct rmi_f30_button),
+				GFP_KERNEL);
+	if (!f30->gpioled_map) {
+		dev_err(&fn->dev, "Failed to allocate button map.\n");
+		return -ENOMEM;
+	}
+
+	pdata = rmi_get_platform_data(rmi_dev);
+	if (pdata) {
+		if (!pdata->gpioled_map) {
+			dev_warn(&fn->dev,
+				"%s - gpioled_map is NULL", __func__);
+		} else if (pdata->gpioled_map->ngpioleds < f30->gpioled_count) {
+			dev_warn(&fn->dev,
+				"Platform Data gpioled map size (%d) is less then the number of buttons on device (%d) - ignored\n",
+				pdata->gpioled_map->ngpioleds,
+				f30->gpioled_count);
+		} else if (!pdata->gpioled_map->map) {
+			dev_warn(&fn->dev,
+				 "Platform Data button map is missing!\n");
+		} else {
+			int i;
+			for (i = 0; i < f30->gpioled_count; i++)
+				memcpy(&f30->gpioled_map[i],
+					&pdata->gpioled_map->map[i],
+					sizeof(struct rmi_f30_button));
+		}
+	}
+
+	retval = rmi_f30_read_control_parameters(rmi_dev, f30);
+	if (retval < 0) {
+		dev_err(&fn->dev,
+			"Failed to initialize F19 control params.\n");
+		return retval;
+	}
+
+	return 0;
+}
+
+static int rmi_f30_probe(struct rmi_function *fn)
+{
+	int rc;
+
+	rc = rmi_f30_initialize(fn);
+	if (rc < 0)
+		goto error_exit;
+
+	rc = rmi_f30_register_device(fn);
+	if (rc < 0)
+		goto error_exit;
+
+	return 0;
+
+error_exit:
+	return rc;
+
+}
+
+static struct rmi_function_handler rmi_f30_handler = {
+	.driver = {
+		.name = "rmi_f30",
+	},
+	.func = 0x30,
+	.probe = rmi_f30_probe,
+	.config = rmi_f30_config,
+	.attention = rmi_f30_attention,
+};
+
+module_rmi_driver(rmi_f30_handler);
+
+MODULE_AUTHOR("Allie Xiong <axiong@synaptics.com>");
+MODULE_AUTHOR("Andrew Duggan <aduggan@synaptics.com>");
+MODULE_DESCRIPTION("RMI F30 module");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
index 735e978..889a627 100644
--- a/include/linux/rmi.h
+++ b/include/linux/rmi.h
@@ -139,9 +139,14 @@ struct rmi_button_map {
 	u8 *map;
 };
 
+struct rmi_f30_button {
+	u16 button;
+	int sense;
+};
+
 struct rmi_f30_gpioled_map {
 	u8 ngpioleds;
-	u8 *map;
+	struct rmi_f30_button *map;
 };
 
 /**

^ permalink raw reply related

* [PATCH] input synaptics-rmi4: rmi_driver.c storage fix
From: Christopher Heiny @ 2014-03-05  3:08 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
	Vivian Ly, Daniel Rosenberg, Linus Walleij, Benjamin Tissoires,
	David Herrmann, Jiri Kosina

Do not use kfree() on storage allocated with devm_kfree(),
eliminating kernel panics on device removal.

I'm not sure how this eluded all the patching that's been going on over
the past few weeks, but somehow it did.

Reported-by: Courtney Cavin <courtney.cavin@sonymobile.com>
Reported-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Reported-by: Andrew Duggan <aduggan@synaptics.com>
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Linux Walleij <linus.walleij@linaro.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>

---

 drivers/input/rmi4/rmi_driver.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index 4406a7f..3552ffb 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -743,7 +743,6 @@ static int rmi_driver_remove(struct device *dev)
 		gpio_free(pdata->attn_gpio);
 
 	kfree(data->irq_status);
-	kfree(data);
 
 	return 0;
 }
@@ -942,7 +941,6 @@ err_destroy_functions:
 err_free_mem:
 	if (data->gpio_held)
 		gpio_free(pdata->attn_gpio);
-	kfree(data);
 	return retval < 0 ? retval : 0;
 }
 

^ permalink raw reply related

* [PATCH 01/02] input synaptics-rmi4: remove rmi_f01 interrupt enable handling
From: Christopher Heiny @ 2014-03-05  3:39 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
	Vivian Ly, Daniel Rosenberg, Linus Walleij, Benjamin Tissoires,
	David Herrmann, Jiri Kosina

This functionality has been moved to rmi_driver.c.

Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Linux Walleij <linus.walleij@linaro.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>

---
 drivers/input/rmi4/rmi_f01.c | 35 +++--------------------------------
 1 file changed, 3 insertions(+), 32 deletions(-)

diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c
index be68ae2..ee5f4a1 100644
--- a/drivers/input/rmi4/rmi_f01.c
+++ b/drivers/input/rmi4/rmi_f01.c
@@ -105,7 +105,6 @@ struct f01_basic_properties {
 
 /**
  * @ctrl0 - see the bit definitions above.
- * @interrupt_enable - A mask of per-function interrupts on the touch sensor.
  * @doze_interval - controls the interval between checks for finger presence
  * when the touch sensor is in doze mode, in units of 10ms.
  * @wakeup_threshold - controls the capacitance threshold at which the touch
@@ -115,7 +114,6 @@ struct f01_basic_properties {
  */
 struct f01_device_control {
 	u8 ctrl0;
-	u8 *interrupt_enable;
 	u8 doze_interval;
 	u8 wakeup_threshold;
 	u8 doze_holdoff;
@@ -126,7 +124,6 @@ struct f01_data {
 
 	struct f01_device_control device_control;
 
-	u16 interrupt_enable_addr;
 	u16 doze_interval_addr;
 	u16 wakeup_threshold_addr;
 	u16 doze_holdoff_addr;
@@ -137,7 +134,6 @@ struct f01_data {
 #endif
 
 	unsigned int num_of_irq_regs;
-	u8 interrupt_enable[];
 };
 
 static int rmi_f01_read_properties(struct rmi_device *rmi_dev,
@@ -186,22 +182,18 @@ static int rmi_f01_probe(struct rmi_function *fn)
 	struct rmi_driver_data *driver_data = dev_get_drvdata(&rmi_dev->dev);
 	const struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev);
 	struct f01_data *f01;
-	size_t f01_size;
 	int error;
 	u16 ctrl_base_addr = fn->fd.control_base_addr;
 	u8 device_status;
 	u8 temp;
 
-	f01_size = sizeof(struct f01_data) +
-				sizeof(u8) * driver_data->num_of_irq_regs;
-	f01 = devm_kzalloc(&fn->dev, f01_size, GFP_KERNEL);
+	f01 = devm_kzalloc(&fn->dev, sizeof(struct f01_data), GFP_KERNEL);
 	if (!f01) {
 		dev_err(&fn->dev, "Failed to allocate fn01_data.\n");
 		return -ENOMEM;
 	}
 
 	f01->num_of_irq_regs = driver_data->num_of_irq_regs;
-	f01->device_control.interrupt_enable = f01->interrupt_enable;
 
 	/*
 	 * Set the configured bit and (optionally) other important stuff
@@ -247,20 +239,6 @@ static int rmi_f01_probe(struct rmi_function *fn)
 		return error;
 	}
 
-	/* Advance to interrupt control registers */
-	ctrl_base_addr++;
-	f01->interrupt_enable_addr = ctrl_base_addr;
-
-	error = rmi_read_block(rmi_dev, f01->interrupt_enable_addr,
-				f01->device_control.interrupt_enable,
-				sizeof(u8) * (f01->num_of_irq_regs));
-	if (error) {
-		dev_err(&fn->dev,
-			"Failed to read F01 control interrupt enable register: %d\n",
-			error);
-		return error;
-	}
-
 	/* Dummy read in order to clear irqs */
 	error = rmi_read(rmi_dev, fn->fd.data_base_addr + 1, &temp);
 	if (error < 0) {
@@ -279,6 +257,8 @@ static int rmi_f01_probe(struct rmi_function *fn)
 		 f01->properties.manufacturer_id == 1 ? "Synaptics" : "unknown",
 		 f01->properties.product_id);
 
+	/* Advance to interrupt control registers, then skip over them. */
+	ctrl_base_addr++;
 	ctrl_base_addr += f01->num_of_irq_regs;
 
 	/* read control register */
@@ -396,15 +376,6 @@ static int rmi_f01_config(struct rmi_function *fn)
 		return error;
 	}
 
-	error = rmi_write_block(fn->rmi_dev, f01->interrupt_enable_addr,
-				f01->device_control.interrupt_enable,
-				sizeof(u8) * f01->num_of_irq_regs);
-	if (error) {
-		dev_err(&fn->dev,
-			"Failed to write interrupt enable: %d\n", error);
-		return error;
-	}
-
 	if (f01->properties.has_adjustable_doze) {
 		error = rmi_write(fn->rmi_dev, f01->doze_interval_addr,
 				  f01->device_control.doze_interval);

^ permalink raw reply related

* [PATCH 0/5] Add ST Keyscan driver
From: Gabriel FERNANDEZ @ 2014-03-05  3:39 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Rob Landley, Russell King, Grant Likely
  Cc: devicetree, linux-doc, linux-kernel, linux-arm-kernel,
	linux-input, kernel, Lee Jones, Gabriel Fernandez

The goal of this series is to add ST Keyscan support to ST SoCs.
The DT definition is added for STiH415 and STiH416 SoCs on
B2000 board.

Gabriel Fernandez (4):
  drivers: input: keyboard: st-keyscan: add keyscan driver
  ARM: STi: DT: add keyscan for stih416
  ARM: STi: DT: add keyscan for stih41x-b2000
  ARM: multi_v7_defconfig: add st-keyscan driver

Giuseppe CONDORELLI (1):
  ARM: STi: DT: add keyscan for stih415

 .../devicetree/bindings/input/st-keypad.txt        |  50 ++++
 arch/arm/boot/dts/stih415-pinctrl.dtsi             |  16 +
 arch/arm/boot/dts/stih415.dtsi                     |  10 +
 arch/arm/boot/dts/stih416-pinctrl.dtsi             |  16 +
 arch/arm/boot/dts/stih416.dtsi                     |  10 +
 arch/arm/boot/dts/stih41x-b2000.dtsi               |  12 +
 arch/arm/configs/multi_v7_defconfig                |   1 +
 drivers/input/keyboard/Kconfig                     |  12 +
 drivers/input/keyboard/Makefile                    |   1 +
 drivers/input/keyboard/st-keyscan.c                | 323 +++++++++++++++++++++
 10 files changed, 451 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/st-keypad.txt
 create mode 100644 drivers/input/keyboard/st-keyscan.c

-- 
1.9.0


^ permalink raw reply

* [PATCH 1/5] drivers: input: keyboard: st-keyscan: add keyscan driver
From: Gabriel FERNANDEZ @ 2014-03-05  3:39 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Rob Landley, Russell King, Grant Likely
  Cc: devicetree, kernel, linux-doc, linux-kernel, Giuseppe Condorelli,
	linux-input, Lee Jones, Gabriel Fernandez, linux-arm-kernel
In-Reply-To: <1393990772-9567-1-git-send-email-gabriel.fernandez@st.com>

This patch adds ST Keyscan driver to use the keypad hw a subset
of ST boards provide. Specific board setup will be put in the
given dt.

Signed-off-by: Giuseppe Condorelli <giuseppe.condorelli@st.com>
Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
---
 .../devicetree/bindings/input/st-keypad.txt        |  50 ++++
 drivers/input/keyboard/Kconfig                     |  12 +
 drivers/input/keyboard/Makefile                    |   1 +
 drivers/input/keyboard/st-keyscan.c                | 323 +++++++++++++++++++++
 4 files changed, 386 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/st-keypad.txt
 create mode 100644 drivers/input/keyboard/st-keyscan.c

diff --git a/Documentation/devicetree/bindings/input/st-keypad.txt b/Documentation/devicetree/bindings/input/st-keypad.txt
new file mode 100644
index 0000000..63eb07a
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/st-keypad.txt
@@ -0,0 +1,50 @@
+* ST Keypad controller device tree bindings
+
+The ST keypad controller device tree binding is based on the
+matrix-keymap.
+
+Required properties:
+
+- compatible: "st,keypad"
+
+- reg: Register base address of st-keypad controler.
+
+- interrupts: Interrupt numberof st-keypad controler.
+
+- clocks: Must contain one entry, for the module clock.
+  See ../clocks/clock-bindings.txt for details.
+
+- pinctrl-0: Should specify pin control groups used for this controller.
+
+- pinctrl-names: Should contain only one value - "default".
+
+- linux,keymap: The keymap for keys as described in the binding document
+  devicetree/bindings/input/matrix-keymap.txt.
+
+- keypad,num-rows: Number of row lines connected to the keypad controller.
+
+- keypad,num-columns: Number of column lines connected to the keypad
+  controller.
+
+- st,debounce_us: Debouncing interval time in microseconds
+
+
+Example:
+
+keyscan: keyscan@fe4b0000 {
+	compatible = "st,keypad";
+	reg = <0xfe4b0000 0x2000>;
+	interrupts = <GIC_SPI 212 IRQ_TYPE_NONE>;
+	clocks	= <&CLK_SYSIN>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_keyscan>;
+
+	keypad,num-rows = <4>;
+	keypad,num-columns = <4>;
+	st,debounce_us = <5000>;
+	linux,keymap = < /* in0	in1	in2	in3 */
+		KEY_F13 KEY_F9  KEY_F5 KEY_F1		/* out0 */
+		KEY_F14 KEY_F10 KEY_F6 KEY_F2		/* out1 */
+		KEY_F15 KEY_F11 KEY_F7 KEY_F3		/* out2 */
+		KEY_F16 KEY_F12 KEY_F8 KEY_F4 >;	/* out3 */
+};
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index a673c9f..9e29125 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -512,6 +512,18 @@ config KEYBOARD_STOWAWAY
 	  To compile this driver as a module, choose M here: the
 	  module will be called stowaway.
 
+config KEYBOARD_ST_KEYSCAN
+	tristate "STMicroelectronics keyscan support"
+	depends on ARCH_STI
+	select INPUT_EVDEV
+	select INPUT_MATRIXKMAP
+	help
+	  Say Y here if you want to use a keypad attached to the keyscan block
+	  on some STMicroelectronics SoC devices.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called stm-keyscan.
+
 config KEYBOARD_SUNKBD
 	tristate "Sun Type 4 and Type 5 keyboard"
 	select SERIO
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index a699b61..5fd020a 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -50,6 +50,7 @@ obj-$(CONFIG_KEYBOARD_SH_KEYSC)		+= sh_keysc.o
 obj-$(CONFIG_KEYBOARD_SPEAR)		+= spear-keyboard.o
 obj-$(CONFIG_KEYBOARD_STMPE)		+= stmpe-keypad.o
 obj-$(CONFIG_KEYBOARD_STOWAWAY)		+= stowaway.o
+obj-$(CONFIG_KEYBOARD_ST_KEYSCAN)	+= st-keyscan.o
 obj-$(CONFIG_KEYBOARD_SUNKBD)		+= sunkbd.o
 obj-$(CONFIG_KEYBOARD_TC3589X)		+= tc3589x-keypad.o
 obj-$(CONFIG_KEYBOARD_TEGRA)		+= tegra-kbc.o
diff --git a/drivers/input/keyboard/st-keyscan.c b/drivers/input/keyboard/st-keyscan.c
new file mode 100644
index 0000000..606cc19
--- /dev/null
+++ b/drivers/input/keyboard/st-keyscan.c
@@ -0,0 +1,323 @@
+/*
+ * STMicroelectronics Key Scanning driver
+ *
+ * Copyright (c) 2014 STMicroelectonics Ltd.
+ * Author: Stuart Menefy <stuart.menefy@st.com>
+ *
+ * Based on sh_keysc.c, copyright 2008 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/input/matrix_keypad.h>
+
+#define ST_KEYSCAN_MAXKEYS 16
+
+#define KEYSCAN_CONFIG_OFF		0x000
+#define KEYSCAN_CONFIG_ENABLE		1
+#define KEYSCAN_DEBOUNCE_TIME_OFF	0x004
+#define KEYSCAN_MATRIX_STATE_OFF	0x008
+#define KEYSCAN_MATRIX_DIM_OFF		0x00c
+#define KEYSCAN_MATRIX_DIM_X_SHIFT	0
+#define KEYSCAN_MATRIX_DIM_Y_SHIFT	2
+
+struct keypad_platform_data {
+	const struct matrix_keymap_data *keymap_data;
+	unsigned int num_out_pads;
+	unsigned int num_in_pads;
+	unsigned int debounce_us;
+};
+
+struct keyscan_priv {
+	void __iomem *base;
+	int irq;
+	struct clk *clk;
+	struct input_dev *input_dev;
+	struct keypad_platform_data *config;
+	unsigned int last_state;
+	u32 keycodes[ST_KEYSCAN_MAXKEYS];
+};
+
+static irqreturn_t keyscan_isr(int irq, void *dev_id)
+{
+	struct platform_device *pdev = dev_id;
+	struct keyscan_priv *priv = platform_get_drvdata(pdev);
+	int state;
+	int change;
+
+	state = readl(priv->base + KEYSCAN_MATRIX_STATE_OFF) & 0xffff;
+	change = priv->last_state ^ state;
+
+	while (change) {
+		int scancode = __ffs(change);
+		int down = state & (1 << scancode);
+
+		input_report_key(priv->input_dev, priv->keycodes[scancode],
+				 down);
+		change ^= 1 << scancode;
+	};
+
+	input_sync(priv->input_dev);
+
+	priv->last_state = state;
+
+	return IRQ_HANDLED;
+}
+
+static void keyscan_start(struct keyscan_priv *priv)
+{
+	clk_enable(priv->clk);
+
+	writel(priv->config->debounce_us * (clk_get_rate(priv->clk) / 1000000),
+	       priv->base + KEYSCAN_DEBOUNCE_TIME_OFF);
+
+	writel(((priv->config->num_in_pads - 1) << KEYSCAN_MATRIX_DIM_X_SHIFT) |
+	       ((priv->config->num_out_pads - 1) << KEYSCAN_MATRIX_DIM_Y_SHIFT),
+	       priv->base + KEYSCAN_MATRIX_DIM_OFF);
+
+	writel(KEYSCAN_CONFIG_ENABLE, priv->base + KEYSCAN_CONFIG_OFF);
+}
+
+static void keyscan_stop(struct keyscan_priv *priv)
+{
+	writel(0, priv->base + KEYSCAN_CONFIG_OFF);
+
+	clk_disable(priv->clk);
+}
+
+static int keypad_matrix_key_parse_dt(struct keyscan_priv *st_kp)
+{
+	struct device *dev = st_kp->input_dev->dev.parent;
+	struct device_node *np = dev->of_node;
+	struct keypad_platform_data *pdata;
+	int error;
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		dev_err(dev, "failed to allocate driver pdata\n");
+		return -ENOMEM;
+	}
+
+	error = matrix_keypad_parse_of_params(dev, &pdata->num_out_pads,
+			&pdata->num_in_pads);
+	if (error) {
+		dev_err(dev, "failed to parse keypad params\n");
+		return error;
+	}
+
+	of_property_read_u32(np, "st,debounce_us", &pdata->debounce_us);
+
+	st_kp->config = pdata;
+
+	dev_info(dev, "rows=%d col=%d debounce=%d\n",
+			pdata->num_out_pads,
+			pdata->num_in_pads,
+			pdata->debounce_us);
+
+	error = of_property_read_u32_array(np, "linux,keymap",
+					st_kp->keycodes, ST_KEYSCAN_MAXKEYS);
+	if (error) {
+		dev_err(dev, "failed to parse keymap\n");
+		return error;
+	}
+
+	return 0;
+}
+
+static int __init keyscan_probe(struct platform_device *pdev)
+{
+	struct keypad_platform_data *pdata =
+		dev_get_platdata(&pdev->dev);
+	struct keyscan_priv *st_kp;
+	struct input_dev *input_dev;
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	int len;
+	int error;
+	int i;
+
+	if (!pdata && !pdev->dev.of_node) {
+		dev_err(&pdev->dev, "no keymap defined\n");
+		return -EINVAL;
+	}
+
+	st_kp = devm_kzalloc(dev, sizeof(*st_kp), GFP_KERNEL);
+	if (!st_kp) {
+		dev_err(dev, "failed to allocate driver data\n");
+		return -ENOMEM;
+	}
+	st_kp->config = pdata;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "no I/O memory specified\n");
+		return -ENXIO;
+	}
+
+	len = resource_size(res);
+	if (!devm_request_mem_region(dev, res->start, len, pdev->name)) {
+		dev_err(dev, "failed to reserve I/O memory\n");
+		return -EBUSY;
+	}
+
+	st_kp->base = devm_ioremap_nocache(dev, res->start, len);
+	if (st_kp->base == NULL) {
+		dev_err(dev, "failed to remap I/O memory\n");
+		return -ENXIO;
+	}
+
+	st_kp->irq = platform_get_irq(pdev, 0);
+	if (st_kp->irq < 0) {
+		dev_err(dev, "no IRQ specified\n");
+		return -ENXIO;
+	}
+
+	error = devm_request_irq(dev, st_kp->irq, keyscan_isr, 0,
+				 pdev->name, pdev);
+	if (error) {
+		dev_err(dev, "failed to request IRQ\n");
+		return error;
+	}
+
+	input_dev = devm_input_allocate_device(&pdev->dev);
+	if (!input_dev) {
+		dev_err(&pdev->dev, "failed to allocate the input device\n");
+		return -ENOMEM;
+	}
+
+	st_kp->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(st_kp->clk)) {
+		dev_err(dev, "cannot get clock");
+		return PTR_ERR(st_kp->clk);
+	}
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		dev_err(dev, "failed to allocate input device\n");
+		return -ENOMEM;
+	}
+	st_kp->input_dev = input_dev;
+
+	input_dev->name = pdev->name;
+	input_dev->phys = "keyscan-keys/input0";
+	input_dev->dev.parent = dev;
+
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->id.vendor = 0x0001;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+
+	if (!pdata) {
+		error = keypad_matrix_key_parse_dt(st_kp);
+		if (error)
+			return error;
+		pdata = st_kp->config;
+	}
+
+	input_dev->keycode = st_kp->keycodes;
+	input_dev->keycodesize = sizeof(st_kp->keycodes[0]);
+	input_dev->keycodemax = ARRAY_SIZE(st_kp->keycodes);
+
+	for (i = 0; i < ST_KEYSCAN_MAXKEYS; i++)
+		input_set_capability(input_dev, EV_KEY, st_kp->keycodes[i]);
+	__clear_bit(KEY_RESERVED, input_dev->keybit);
+
+	error = input_register_device(input_dev);
+	if (error) {
+		dev_err(dev, "failed to register input device\n");
+		input_free_device(input_dev);
+		platform_set_drvdata(pdev, NULL);
+		return error;
+	}
+
+	platform_set_drvdata(pdev, st_kp);
+
+	keyscan_start(st_kp);
+
+	device_set_wakeup_capable(&pdev->dev, 1);
+
+	return 0;
+}
+
+static int __exit keyscan_remove(struct platform_device *pdev)
+{
+	struct keyscan_priv *priv = platform_get_drvdata(pdev);
+
+	keyscan_stop(priv);
+
+	input_unregister_device(priv->input_dev);
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static int keyscan_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct keyscan_priv *priv = platform_get_drvdata(pdev);
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(priv->irq);
+	else
+		keyscan_stop(priv);
+
+	return 0;
+}
+
+static int keyscan_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct keyscan_priv *priv = platform_get_drvdata(pdev);
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(priv->irq);
+	else
+		keyscan_start(priv);
+
+	return 0;
+}
+
+static const struct dev_pm_ops keyscan_dev_pm_ops = {
+	.suspend = keyscan_suspend,
+	.resume = keyscan_resume,
+};
+
+static const struct of_device_id keyscan_of_match[] = {
+	{ .compatible = "st,keypad" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, keyscan_of_match);
+
+__refdata struct platform_driver keyscan_device_driver = {
+	.probe		= keyscan_probe,
+	.remove		= __exit_p(keyscan_remove),
+	.driver		= {
+		.name	= "st-keyscan",
+		.pm	= &keyscan_dev_pm_ops,
+		.of_match_table = of_match_ptr(keyscan_of_match),
+	}
+};
+
+static int __init keyscan_init(void)
+{
+	return platform_driver_register(&keyscan_device_driver);
+}
+
+static void __exit keyscan_exit(void)
+{
+	platform_driver_unregister(&keyscan_device_driver);
+}
+
+module_init(keyscan_init);
+module_exit(keyscan_exit);
+
+MODULE_AUTHOR("Stuart Menefy <stuart.menefy@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics keyscan device driver");
+MODULE_LICENSE("GPL");
-- 
1.9.0

^ permalink raw reply related

* [PATCH 2/5] ARM: STi: DT: add keyscan for stih415
From: Gabriel FERNANDEZ @ 2014-03-05  3:39 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Rob Landley, Russell King, Grant Likely
  Cc: devicetree, linux-doc, linux-kernel, linux-arm-kernel,
	linux-input, kernel, Lee Jones, Giuseppe CONDORELLI,
	Gabriel Fernandez
In-Reply-To: <1393990772-9567-1-git-send-email-gabriel.fernandez@st.com>

From: Giuseppe CONDORELLI <giuseppe.condorelli@st.com>

Add keyscan support for stih415.
It is put disabled by default because it is not enabled on all boards
Also there are PIOs conflict with already claimed lines.

Signed-off-by: Giuseppe Condorelli <giuseppe.condorelli@st.com>
Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
---
 arch/arm/boot/dts/stih415-pinctrl.dtsi | 16 ++++++++++++++++
 arch/arm/boot/dts/stih415.dtsi         | 10 ++++++++++
 2 files changed, 26 insertions(+)

diff --git a/arch/arm/boot/dts/stih415-pinctrl.dtsi b/arch/arm/boot/dts/stih415-pinctrl.dtsi
index e56449d..731e4b1 100644
--- a/arch/arm/boot/dts/stih415-pinctrl.dtsi
+++ b/arch/arm/boot/dts/stih415-pinctrl.dtsi
@@ -87,6 +87,22 @@
 				};
 			};
 
+			keyscan {
+				pinctrl_keyscan: keyscan {
+					st,pins {
+						keyin0  = <&PIO0 2 ALT2 IN>;
+						keyin1  = <&PIO0 3 ALT2 IN>;
+						keyin2  = <&PIO0 4 ALT2 IN>;
+						keyin3  = <&PIO2 6 ALT2 IN>;
+
+						keyout0 = <&PIO1 6 ALT2 OUT>;
+						keyout1 = <&PIO1 7 ALT2 OUT>;
+						keyout2 = <&PIO0 6 ALT2 OUT>;
+						keyout3 = <&PIO2 7 ALT2 OUT>;
+					};
+				};
+			};
+
 			sbc_i2c0 {
 				pinctrl_sbc_i2c0_default: sbc_i2c0-default {
 					st,pins {
diff --git a/arch/arm/boot/dts/stih415.dtsi b/arch/arm/boot/dts/stih415.dtsi
index d9c7dd1..5930467 100644
--- a/arch/arm/boot/dts/stih415.dtsi
+++ b/arch/arm/boot/dts/stih415.dtsi
@@ -136,5 +136,15 @@
 
 			status		= "disabled";
 		};
+
+		keyscan: keyscan@fe4b0000 {
+			compatible = "st,keypad";
+			status = "disabled";
+			reg = <0xfe4b0000 0x2000>;
+			interrupts = <GIC_SPI 212 IRQ_TYPE_NONE>;
+			clocks	= <&CLK_SYSIN>;
+			pinctrl-names	= "default";
+			pinctrl-0 = <&pinctrl_keyscan>;
+		};
 	};
 };
-- 
1.9.0


^ permalink raw reply related

* [PATCH 3/5] ARM: STi: DT: add keyscan for stih416
From: Gabriel FERNANDEZ @ 2014-03-05  3:39 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Rob Landley, Russell King, Grant Likely
  Cc: devicetree, linux-doc, linux-kernel, linux-arm-kernel,
	linux-input, kernel, Lee Jones, Gabriel Fernandez,
	Giuseppe Condorelli
In-Reply-To: <1393990772-9567-1-git-send-email-gabriel.fernandez@st.com>

Add keyscan support for stih416.
It is disabled by default given that it is not enabled on all boards.
Also there are PIOs conflict with already claimed lines.

Signed-off-by: Giuseppe Condorelli <giuseppe.condorelli@st.com>
Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
---
 arch/arm/boot/dts/stih416-pinctrl.dtsi | 16 ++++++++++++++++
 arch/arm/boot/dts/stih416.dtsi         | 10 ++++++++++
 2 files changed, 26 insertions(+)

diff --git a/arch/arm/boot/dts/stih416-pinctrl.dtsi b/arch/arm/boot/dts/stih416-pinctrl.dtsi
index b29ff4b..4e0ebed 100644
--- a/arch/arm/boot/dts/stih416-pinctrl.dtsi
+++ b/arch/arm/boot/dts/stih416-pinctrl.dtsi
@@ -98,6 +98,22 @@
 				};
 			};
 
+			keyscan {
+				pinctrl_keyscan: keyscan {
+					st,pins {
+						keyin0	= <&PIO0 2 ALT2 IN>;
+						keyin1	= <&PIO0 3 ALT2 IN>;
+						keyin2	= <&PIO0 4 ALT2 IN>;
+						keyin3	= <&PIO2 6 ALT2 IN>;
+
+						keyout0 = <&PIO1 6 ALT2 OUT>;
+						keyout1 = <&PIO1 7 ALT2 OUT>;
+						keyout2 = <&PIO0 6 ALT2 OUT>;
+						keyout3 = <&PIO2 7 ALT2 OUT>;
+					};
+				};
+			};
+
 			sbc_i2c0 {
 				pinctrl_sbc_i2c0_default: sbc_i2c0-default {
 					st,pins {
diff --git a/arch/arm/boot/dts/stih416.dtsi b/arch/arm/boot/dts/stih416.dtsi
index b7ab47b..9f20b21 100644
--- a/arch/arm/boot/dts/stih416.dtsi
+++ b/arch/arm/boot/dts/stih416.dtsi
@@ -145,5 +145,15 @@
 
 			status		= "disabled";
 		};
+
+		keyscan: keyscan@fe4b0000 {
+			compatible = "st,keypad";
+			status = "disabled";
+			reg = <0xfe4b0000 0x2000>;
+			interrupts = <GIC_SPI 212 IRQ_TYPE_NONE>;
+			clocks	= <&CLK_SYSIN>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pinctrl_keyscan>;
+		};
 	};
 };
-- 
1.9.0

^ permalink raw reply related

* [PATCH 4/5] ARM: STi: DT: add keyscan for stih41x-b2000
From: Gabriel FERNANDEZ @ 2014-03-05  3:39 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Rob Landley, Russell King, Grant Likely
  Cc: devicetree, linux-doc, linux-kernel, linux-arm-kernel,
	linux-input, kernel, Lee Jones, Gabriel Fernandez,
	Giuseppe Condorelli
In-Reply-To: <1393990772-9567-1-git-send-email-gabriel.fernandez@st.com>

Add keyscan setup for stih415/h416 b2000.
Both have same raw/column lines number, debounce time and keymap.

Signed-off-by: Giuseppe Condorelli <giuseppe.condorelli@st.com>
Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
---
 arch/arm/boot/dts/stih41x-b2000.dtsi | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm/boot/dts/stih41x-b2000.dtsi b/arch/arm/boot/dts/stih41x-b2000.dtsi
index 1e6aa92..cf06beb 100644
--- a/arch/arm/boot/dts/stih41x-b2000.dtsi
+++ b/arch/arm/boot/dts/stih41x-b2000.dtsi
@@ -6,6 +6,7 @@
  * it under the terms of the GNU General Public License version 2 as
  * publishhed by the Free Software Foundation.
  */
+#include <dt-bindings/input/input.h>
 / {
 
 	memory{
@@ -46,5 +47,16 @@
 
 			status = "okay";
 		};
+
+		keyscan: keyscan@fe4b0000 {
+			keypad,num-rows = <4>;
+			keypad,num-columns = <4>;
+			st,debounce_us = <5000>;
+			linux,keymap = < /* in0	in1	in2	in3 */
+				KEY_F13 KEY_F9  KEY_F5 KEY_F1		/* out0 */
+				KEY_F14 KEY_F10 KEY_F6 KEY_F2		/* out1 */
+				KEY_F15 KEY_F11 KEY_F7 KEY_F3		/* out2 */
+				KEY_F16 KEY_F12 KEY_F8 KEY_F4 >;	/* out3 */
+		};
 	};
 };
-- 
1.9.0

^ permalink raw reply related

* [PATCH 5/5] ARM: multi_v7_defconfig: add st-keyscan driver
From: Gabriel FERNANDEZ @ 2014-03-05  3:39 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Rob Landley, Russell King, Grant Likely
  Cc: devicetree, linux-doc, linux-kernel, linux-arm-kernel,
	linux-input, kernel, Lee Jones, Gabriel Fernandez
In-Reply-To: <1393990772-9567-1-git-send-email-gabriel.fernandez@st.com>

This patch adds KEYBOARD_ST_KEYSCAN config

Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
---
 arch/arm/configs/multi_v7_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index ee69829..5e926981 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -131,6 +131,7 @@ CONFIG_INPUT_EVDEV=y
 CONFIG_KEYBOARD_GPIO=y
 CONFIG_KEYBOARD_TEGRA=y
 CONFIG_KEYBOARD_SPEAR=y
+CONFIG_KEYBOARD_ST_KEYSCAN=y
 CONFIG_KEYBOARD_CROS_EC=y
 CONFIG_MOUSE_PS2_ELANTECH=y
 CONFIG_INPUT_MISC=y
-- 
1.9.0

^ permalink raw reply related

* [PATCH 02/02] input synaptics-rmi4: improved interrupt enable management
From: Christopher Heiny @ 2014-03-05  3:39 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
	Vivian Ly, Daniel Rosenberg, Linus Walleij, Benjamin Tissoires,
	David Herrmann, Jiri Kosina
In-Reply-To: <1393990760-2587-1-git-send-email-cheiny@synaptics.com>

The previous interrupt save/restore routine was inflexible.  Remove
that and add set_bits() and clear_bits() functions, allowing simpler
management of individual function interrupt enables.

Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Linux Walleij <linus.walleij@linaro.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>

---

 drivers/input/rmi4/rmi_bus.h    |  12 ++---
 drivers/input/rmi4/rmi_driver.c | 104 ++++++++++++++++------------------------
 drivers/input/rmi4/rmi_driver.h |   3 +-
 3 files changed, 48 insertions(+), 71 deletions(-)

diff --git a/drivers/input/rmi4/rmi_bus.h b/drivers/input/rmi4/rmi_bus.h
index 1672b05..d4cfc85 100644
--- a/drivers/input/rmi4/rmi_bus.h
+++ b/drivers/input/rmi4/rmi_bus.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2013 Synaptics Incorporated
+ * Copyright (c) 2011-2014 Synaptics Incorporated
  * Copyright (c) 2011 Unixphere
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -111,9 +111,8 @@ void rmi_unregister_function_handler(struct rmi_function_handler *);
  * @driver: Device driver model driver
  * @irq_handler: Callback for handling irqs
  * @reset_handler: Called when a reset is detected.
- * @get_func_irq_mask: Callback for calculating interrupt mask
- * @store_irq_mask: Callback for storing and replacing interrupt mask
- * @restore_irq_mask: Callback for restoring previously stored interrupt mask
+ * @clear_irq_bits: Clear the specified bits in the current interrupt mask.
+ * @set_irq_bist: Set the specified bits in the current interrupt mask.
  * @store_productid: Callback for cache product id from function 01
  * @data: Private data pointer
  *
@@ -123,9 +122,8 @@ struct rmi_driver {
 
 	int (*irq_handler)(struct rmi_device *rmi_dev, int irq);
 	int (*reset_handler)(struct rmi_device *rmi_dev);
-	int (*store_irq_mask)(struct rmi_device *rmi_dev,
-				unsigned long *new_interupts);
-	int (*restore_irq_mask)(struct rmi_device *rmi_dev);
+	int (*clear_irq_bits)(struct rmi_device *rmi_dev, unsigned long *mask);
+	int (*set_irq_bits)(struct rmi_device *rmi_dev, unsigned long *mask);
 	int (*store_productid)(struct rmi_device *rmi_dev);
 	int (*set_input_params)(struct rmi_device *rmi_dev,
 			struct input_dev *input);
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index 4406a7f..2172c80 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -330,78 +330,56 @@ static int rmi_driver_set_input_params(struct rmi_device *rmi_dev,
 	return 0;
 }
 
-/**
- * This pair of functions allows functions like function 54 to request to have
- * other interrupts disabled until the restore function is called. Only one
- * store happens at a time.
- */
-static int rmi_driver_irq_save(struct rmi_device *rmi_dev,
-				unsigned long *new_ints)
+static int rmi_driver_set_irq_bits(struct rmi_device *rmi_dev,
+				   unsigned long *mask)
 {
-	int retval = 0;
+	int error = 0;
 	struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
 	struct device *dev = &rmi_dev->dev;
 
 	mutex_lock(&data->irq_mutex);
-	if (!data->irq_stored) {
-		/* Save current enabled interrupts */
-		retval = rmi_read_block(rmi_dev,
-				data->f01_container->fd.control_base_addr + 1,
-				data->irq_mask_store, data->num_of_irq_regs);
-		if (retval < 0) {
-			dev_err(dev, "%s: Failed to read enabled interrupts!",
-								__func__);
-			goto error_unlock;
-		}
-		retval = rmi_write_block(rmi_dev,
-				data->f01_container->fd.control_base_addr + 1,
-				new_ints, data->num_of_irq_regs);
-		if (retval < 0) {
-			dev_err(dev, "%s: Failed to change enabled interrupts!",
-								__func__);
-			goto error_unlock;
-		}
-		bitmap_copy(data->current_irq_mask, new_ints, data->irq_count);
-		data->irq_stored = true;
-	} else {
-		retval = -ENOSPC; /* No space to store IRQs.*/
-		dev_err(dev, "Attempted to save IRQs when already stored!");
+	bitmap_or(data->new_irq_mask,
+		  data->current_irq_mask, mask, data->irq_count);
+	error = rmi_write_block(rmi_dev,
+			data->f01_container->fd.control_base_addr + 1,
+			data->new_irq_mask, data->num_of_irq_regs);
+	if (error < 0) {
+		dev_err(dev, "%s: Failed to change enabled interrupts!",
+							__func__);
+		goto error_unlock;
 	}
+	bitmap_copy(data->current_irq_mask, data->new_irq_mask,
+		    data->num_of_irq_regs);
 
 error_unlock:
 	mutex_unlock(&data->irq_mutex);
-	return retval;
+	return error;
 }
 
-static int rmi_driver_irq_restore(struct rmi_device *rmi_dev)
+static int rmi_driver_clear_irq_bits(struct rmi_device *rmi_dev,
+				     unsigned long *mask)
 {
-	int retval = 0;
+	int error = 0;
 	struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
 	struct device *dev = &rmi_dev->dev;
 
 	mutex_lock(&data->irq_mutex);
-
-	if (data->irq_stored) {
-		retval = rmi_write_block(rmi_dev,
-				data->f01_container->fd.control_base_addr+1,
-				data->irq_mask_store, data->num_of_irq_regs);
-		if (retval < 0) {
-			dev_err(dev, "%s: Failed to write enabled interupts!",
-								__func__);
-			goto error_unlock;
-		}
-		memcpy(data->current_irq_mask, data->irq_mask_store,
-					data->num_of_irq_regs * sizeof(u8));
-		data->irq_stored = false;
-	} else {
-		retval = -EINVAL;
-		dev_err(dev, "%s: Attempted to restore values when not stored!",
-			__func__);
+	bitmap_andnot(data->new_irq_mask,
+		  data->current_irq_mask, mask, data->irq_count);
+	error = rmi_write_block(rmi_dev,
+			data->f01_container->fd.control_base_addr + 1,
+			data->new_irq_mask, data->num_of_irq_regs);
+	if (error < 0) {
+		dev_err(dev, "%s: Failed to change enabled interrupts!",
+							__func__);
+		goto error_unlock;
 	}
+	bitmap_copy(data->current_irq_mask, data->new_irq_mask,
+		    data->num_of_irq_regs);
 
 error_unlock:
 	mutex_unlock(&data->irq_mutex);
-	return retval;
+	return error;
 }
 
 static int rmi_driver_irq_handler(struct rmi_device *rmi_dev, int irq)
@@ -437,21 +415,23 @@ static int rmi_driver_reset_handler(struct rmi_device *rmi_dev)
 		return 0;
 	}
 
+	error = rmi_read_block(rmi_dev,
+			       data->f01_container->fd.control_base_addr + 1,
+			       data->current_irq_mask, data->num_of_irq_regs);
+	if (error < 0) {
+		dev_err(&rmi_dev->dev, "%s: Failed to read current IRQ mask.\n",
+			__func__);
+		return error;
+	}
+
 	error = rmi_driver_process_reset_requests(rmi_dev);
 	if (error < 0)
 		return error;
 
-
 	error = rmi_driver_process_config_requests(rmi_dev);
 	if (error < 0)
 		return error;
 
-	if (data->irq_stored) {
-		error = rmi_driver_irq_restore(rmi_dev);
-		if (error < 0)
-			return error;
-	}
-
 	return 0;
 }
 
@@ -846,7 +826,7 @@ static int rmi_driver_probe(struct device *dev)
 	data->irq_status	= irq_memory + size * 0;
 	data->fn_irq_bits	= irq_memory + size * 1;
 	data->current_irq_mask	= irq_memory + size * 2;
-	data->irq_mask_store	= irq_memory + size * 3;
+	data->new_irq_mask	= irq_memory + size * 3;
 
 	irq_count = 0;
 	dev_dbg(dev, "Creating functions.");
@@ -957,8 +937,8 @@ static struct rmi_driver rmi_physical_driver = {
 	},
 	.irq_handler = rmi_driver_irq_handler,
 	.reset_handler = rmi_driver_reset_handler,
-	.store_irq_mask = rmi_driver_irq_save,
-	.restore_irq_mask = rmi_driver_irq_restore,
+	.clear_irq_bits = rmi_driver_clear_irq_bits,
+	.set_irq_bits = rmi_driver_set_irq_bits,
 	.set_input_params = rmi_driver_set_input_params,
 };
 
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index a22a4e6..34f7a7d 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -47,8 +47,7 @@ struct rmi_driver_data {
 	unsigned long *irq_status;
 	unsigned long *fn_irq_bits;
 	unsigned long *current_irq_mask;
-	unsigned long *irq_mask_store;
-	bool irq_stored;
+	unsigned long *new_irq_mask;
 	struct mutex irq_mutex;
 
 	/* Following are used when polling. */

^ 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