Skip to content

Commit

Permalink
Input: mpr121 - add polling mode
Browse files Browse the repository at this point in the history
In case the interrupt line is not available, polling can be used
to read out the state of the keys. Period of the polling needs to
be configured by the poll-interval DT property.

Signed-off-by: Michal Vokáč <michal.vokac@ysoft.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
  • Loading branch information
Michal Vokáč authored and Dmitry Torokhov committed Oct 16, 2019
1 parent def10ec commit ee358cb
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 19 deletions.
25 changes: 24 additions & 1 deletion Documentation/devicetree/bindings/input/fsl,mpr121-touchkey.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ description: |
allOf:
- $ref: input.yaml#

anyOf:
- required: [ interrupts ]
- required: [ poll-interval ]

properties:
compatible:
const: fsl,mpr121-touchkey
Expand All @@ -41,12 +45,12 @@ properties:
required:
- compatible
- reg
- interrupts
- vdd-supply
- linux,keycodes

examples:
- |
// Example with interrupts
#include "dt-bindings/input/input.h"
i2c {
#address-cells = <1>;
Expand All @@ -64,3 +68,22 @@ examples:
<KEY_8>, <KEY_9>, <KEY_A>, <KEY_B>;
};
};
- |
// Example with polling
#include "dt-bindings/input/input.h"
i2c {
#address-cells = <1>;
#size-cells = <0>;
mpr121@5a {
compatible = "fsl,mpr121-touchkey";
reg = <0x5a>;
poll-interval = <20>;
autorepeat;
vdd-supply = <&ldo4_reg>;
linux,keycodes = <KEY_0>, <KEY_1>, <KEY_2>, <KEY_3>,
<KEY_4>, <KEY_5>, <KEY_6>, <KEY_7>,
<KEY_8>, <KEY_9>, <KEY_A>, <KEY_B>;
};
};
4 changes: 4 additions & 0 deletions Documentation/devicetree/bindings/input/input.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ properties:
minimum: 0
maximum: 0xff

poll-interval:
description: Poll interval time in milliseconds.
$ref: /schemas/types.yaml#/definitions/uint32

power-off-time-sec:
description:
Duration in seconds which the key should be kept pressed for device to
Expand Down
69 changes: 51 additions & 18 deletions drivers/input/keyboard/mpr121_touchkey.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@
/* MPR121 has 12 keys */
#define MPR121_MAX_KEY_COUNT 12

#define MPR121_MIN_POLL_INTERVAL 10
#define MPR121_MAX_POLL_INTERVAL 200

struct mpr121_touchkey {
struct i2c_client *client;
struct input_dev *input_dev;
Expand Down Expand Up @@ -115,26 +118,26 @@ static struct regulator *mpr121_vdd_supply_init(struct device *dev)
return vdd_supply;
}

static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id)
static void mpr_touchkey_report(struct input_dev *dev)
{
struct mpr121_touchkey *mpr121 = dev_id;
struct i2c_client *client = mpr121->client;
struct mpr121_touchkey *mpr121 = input_get_drvdata(dev);
struct input_dev *input = mpr121->input_dev;
struct i2c_client *client = mpr121->client;
unsigned long bit_changed;
unsigned int key_num;
int reg;

reg = i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_1_ADDR);
if (reg < 0) {
dev_err(&client->dev, "i2c read error [%d]\n", reg);
goto out;
return;
}

reg <<= 8;
reg |= i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_0_ADDR);
if (reg < 0) {
dev_err(&client->dev, "i2c read error [%d]\n", reg);
goto out;
return;
}

reg &= TOUCH_STATUS_MASK;
Expand All @@ -155,8 +158,14 @@ static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id)

}
input_sync(input);
}

static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id)
{
struct mpr121_touchkey *mpr121 = dev_id;

mpr_touchkey_report(mpr121->input_dev);

out:
return IRQ_HANDLED;
}

Expand Down Expand Up @@ -229,14 +238,10 @@ static int mpr_touchkey_probe(struct i2c_client *client,
int vdd_uv;
struct mpr121_touchkey *mpr121;
struct input_dev *input_dev;
u32 poll_interval = 0;
int error;
int i;

if (!client->irq) {
dev_err(dev, "irq number should not be zero\n");
return -EINVAL;
}

vdd_supply = mpr121_vdd_supply_init(dev);
if (IS_ERR(vdd_supply))
return PTR_ERR(vdd_supply);
Expand Down Expand Up @@ -274,6 +279,7 @@ static int mpr_touchkey_probe(struct i2c_client *client,
if (device_property_read_bool(dev, "autorepeat"))
__set_bit(EV_REP, input_dev->evbit);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
input_set_drvdata(input_dev, mpr121);

input_dev->keycode = mpr121->keycodes;
input_dev->keycodesize = sizeof(mpr121->keycodes[0]);
Expand All @@ -288,13 +294,40 @@ static int mpr_touchkey_probe(struct i2c_client *client,
return error;
}

error = devm_request_threaded_irq(dev, client->irq, NULL,
mpr_touchkey_interrupt,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
dev->driver->name, mpr121);
if (error) {
dev_err(dev, "Failed to register interrupt\n");
return error;
device_property_read_u32(dev, "poll-interval", &poll_interval);

if (client->irq) {
error = devm_request_threaded_irq(dev, client->irq, NULL,
mpr_touchkey_interrupt,
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
dev->driver->name, mpr121);
if (error) {
dev_err(dev, "Failed to register interrupt\n");
return error;
}
} else if (poll_interval) {
if (poll_interval < MPR121_MIN_POLL_INTERVAL)
return -EINVAL;

if (poll_interval > MPR121_MAX_POLL_INTERVAL)
return -EINVAL;

error = input_setup_polling(input_dev, mpr_touchkey_report);
if (error) {
dev_err(dev, "Failed to setup polling\n");
return error;
}

input_set_poll_interval(input_dev, poll_interval);
input_set_min_poll_interval(input_dev,
MPR121_MIN_POLL_INTERVAL);
input_set_max_poll_interval(input_dev,
MPR121_MAX_POLL_INTERVAL);
} else {
dev_err(dev,
"invalid IRQ number and polling not configured\n");
return -EINVAL;
}

error = input_register_device(input_dev);
Expand Down

0 comments on commit ee358cb

Please sign in to comment.