--- v1
+++ v6
@@ -1,21 +1,24 @@
This patch adds support for the TM2 touch key and led
-functionlity.
+functionality.
The driver interfaces with userspace through an input device and
reports KEY_PHONE and KEY_BACK event types. LED brightness can be
controlled by "/sys/class/leds/tm2-touchkey/brightness".
+Signed-off-by: Beomho Seo <beomho.seo@samsung.com>
Signed-off-by: Jaechul Lee <jcsing.lee@samsung.com>
-Signed-off-by: Beomho Seo <beomho.seo@samsung.com>
+Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com>
+Reviewed-by: Andi Shyti <andi.shyti@samsung.com>
+Acked-by: Krzysztof Kozlowski <krzk@kernel.org>
---
drivers/input/keyboard/Kconfig | 11 ++
drivers/input/keyboard/Makefile | 1 +
- drivers/input/keyboard/tm2-touchkey.c | 326 ++++++++++++++++++++++++++++++++++
- 3 files changed, 338 insertions(+)
+ drivers/input/keyboard/tm2-touchkey.c | 287 ++++++++++++++++++++++++++++++++++
+ 3 files changed, 299 insertions(+)
create mode 100644 drivers/input/keyboard/tm2-touchkey.c
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
-index cbd75cf..72c0ba1 100644
+index cbd75cf..97acd65 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -666,6 +666,17 @@ config KEYBOARD_TC3589X
@@ -23,15 +26,15 @@
module will be called tc3589x-keypad.
+config KEYBOARD_TM2_TOUCHKEY
-+ tristate "tm2-touchkey support"
++ tristate "TM2 touchkey support"
+ depends on I2C
++ depends on LEDS_CLASS
+ help
-+ Say Y here to enable the tm2-touchkey.
-+ touchkey driver for tm2. This driver can enable
-+ the interrupt and make input events and control led brightness.
++ Say Y here to enable device driver for tm2-touchkey with
++ LED control for the Exynos5433 TM2 board.
+
+ To compile this driver as a module, choose M here.
-+ module will be called tm2-touchkey
++ module will be called tm2-touchkey.
+
config KEYBOARD_TWL4030
tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
@@ -50,12 +53,12 @@
obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o
diff --git a/drivers/input/keyboard/tm2-touchkey.c b/drivers/input/keyboard/tm2-touchkey.c
new file mode 100644
-index 0000000..d9575d8
+index 0000000..e927d06
--- /dev/null
+++ b/drivers/input/keyboard/tm2-touchkey.c
-@@ -0,0 +1,326 @@
+@@ -0,0 +1,287 @@
+/*
-+ * Driver for keys on GPIO lines capable of generating interrupts.
++ * TM2 touchkey device driver
+ *
+ * Copyright 2005 Phil Blundell
+ * Copyright 2016 Samsung Electronics Co., Ltd.
@@ -80,44 +83,41 @@
+#include <linux/of.h>
+#include <linux/pm.h>
+#include <linux/regulator/consumer.h>
-+#include <linux/workqueue.h>
-+
-+#define TM2_TOUCHKEY_DEV_NAME "tm2-touchkey"
-+#define TM2_TOUCHKEY_KEYCODE_REG 0x03
-+#define TM2_TOUCHKEY_BASE_REG 0x00
-+#define TM2_TOUCHKEY_CMD_LED_ON 0x10
-+#define TM2_TOUCHKEY_CMD_LED_OFF 0x20
-+#define TM2_TOUCHKEY_BIT_PRESS_EV BIT(3)
-+#define TM2_TOUCHKEY_BIT_KEYCODE GENMASK(2, 0)
-+#define TM2_TOUCHKEY_LED_VOLTAGE_MIN 2500000
-+#define TM2_TOUCHKEY_LED_VOLTAGE_MAX 3300000
++
++#define TM2_TOUCHKEY_DEV_NAME "tm2-touchkey"
++#define TM2_TOUCHKEY_KEYCODE_REG 0x03
++#define TM2_TOUCHKEY_BASE_REG 0x00
++#define TM2_TOUCHKEY_CMD_LED_ON 0x10
++#define TM2_TOUCHKEY_CMD_LED_OFF 0x20
++#define TM2_TOUCHKEY_BIT_PRESS_EV BIT(3)
++#define TM2_TOUCHKEY_BIT_KEYCODE GENMASK(2, 0)
++#define TM2_TOUCHKEY_LED_VOLTAGE_MIN 2500000
++#define TM2_TOUCHKEY_LED_VOLTAGE_MAX 3300000
+
+enum {
+ TM2_TOUCHKEY_KEY_MENU = 0x1,
+ TM2_TOUCHKEY_KEY_BACK,
+};
+
-+#define tm2_touchkey_power_enable(x) __tm2_touchkey_power_onoff(x, 1)
-+#define tm2_touchkey_power_disable(x) __tm2_touchkey_power_onoff(x, 0)
++enum {
++ TM2_TOUCHKEY_SUPPLIES_VCC,
++ TM2_TOUCHKEY_SUPPLIES_VDD,
++};
+
+struct tm2_touchkey_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct led_classdev led_dev;
++ struct regulator_bulk_data regulators[2];
+
+ u8 keycode_type;
+ u8 pressed;
-+ struct work_struct irq_work;
-+
-+ bool power_onoff;
-+ struct regulator *regulator_vcc; /* 1.8V */
-+ struct regulator *regulator_vdd; /* 3.3V */
+};
+
+static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev,
+ enum led_brightness brightness)
+{
-+ struct tm2_touchkey_data *samsung_touchkey =
++ struct tm2_touchkey_data *touchkey =
+ container_of(led_dev, struct tm2_touchkey_data, led_dev);
+ u32 volt;
+ u8 data;
@@ -130,94 +130,79 @@
+ data = TM2_TOUCHKEY_CMD_LED_ON;
+ }
+
-+ regulator_set_voltage(samsung_touchkey->regulator_vdd, volt, volt);
-+ i2c_smbus_write_byte_data(samsung_touchkey->client,
-+ TM2_TOUCHKEY_BASE_REG, data);
-+}
-+
-+static int __tm2_touchkey_power_onoff(struct tm2_touchkey_data
-+ *samsung_touchkey, bool onoff)
++ regulator_set_voltage(
++ touchkey->regulators[TM2_TOUCHKEY_SUPPLIES_VDD].consumer,
++ volt, volt);
++ i2c_smbus_write_byte_data(touchkey->client,
++ TM2_TOUCHKEY_BASE_REG, data);
++}
++
++static int tm2_touchkey_power_enable(struct tm2_touchkey_data *touchkey)
+{
+ int ret = 0;
+
-+ if (samsung_touchkey->power_onoff == onoff)
-+ return ret;
-+
-+ if (onoff) {
-+ ret = regulator_enable(samsung_touchkey->regulator_vcc);
-+ if (ret)
-+ return ret;
-+
-+ ret = regulator_enable(samsung_touchkey->regulator_vdd);
-+ if (ret) {
-+ regulator_disable(samsung_touchkey->regulator_vcc);
-+ return ret;
-+ }
-+ msleep(150);
++ ret = regulator_bulk_enable(ARRAY_SIZE(touchkey->regulators),
++ touchkey->regulators);
++ if (ret)
++ return ret;
++
++ /* waiting for device initialization, at least 150ms */
++ msleep(150);
++
++ return 0;
++}
++
++static void tm2_touchkey_power_disable(void *data)
++{
++ struct tm2_touchkey_data *touchkey = data;
++
++ regulator_bulk_disable(ARRAY_SIZE(touchkey->regulators),
++ touchkey->regulators);
++}
++
++static irqreturn_t tm2_touchkey_irq_handler(int irq, void *devid)
++{
++ struct tm2_touchkey_data *touchkey = devid;
++ u32 data;
++
++ data = i2c_smbus_read_byte_data(touchkey->client,
++ TM2_TOUCHKEY_KEYCODE_REG);
++
++ if (data < 0) {
++ dev_err(&touchkey->client->dev, "Failed to read i2c data\n");
++ return IRQ_HANDLED;
++ }
++
++ touchkey->keycode_type = data & TM2_TOUCHKEY_BIT_KEYCODE;
++ touchkey->pressed = !(data & TM2_TOUCHKEY_BIT_PRESS_EV);
++
++ if (touchkey->keycode_type != TM2_TOUCHKEY_KEY_MENU &&
++ touchkey->keycode_type != TM2_TOUCHKEY_KEY_BACK) {
++ dev_warn(&touchkey->client->dev, "Skip unhandled keycode(%d)\n",
++ touchkey->keycode_type);
++ return IRQ_HANDLED;
++ }
++
++ if (!touchkey->pressed) {
++ input_report_key(touchkey->input_dev, KEY_PHONE, 0);
++ input_report_key(touchkey->input_dev, KEY_BACK, 0);
+ } else {
-+ int err;
-+
-+ err = regulator_disable(samsung_touchkey->regulator_vcc);
-+ if (err)
-+ ret = err;
-+
-+ err = regulator_disable(samsung_touchkey->regulator_vdd);
-+ if (err && !ret)
-+ ret = err;
-+ }
-+ samsung_touchkey->power_onoff = onoff;
-+
-+ return ret;
-+}
-+
-+static void tm2_touchkey_irq_work(struct work_struct *irq_work)
-+{
-+ struct tm2_touchkey_data *samsung_touchkey =
-+ container_of(irq_work, struct tm2_touchkey_data, irq_work);
-+
-+ if (!samsung_touchkey->pressed) {
-+ input_report_key(samsung_touchkey->input_dev, KEY_PHONE, 0);
-+ input_report_key(samsung_touchkey->input_dev, KEY_BACK, 0);
-+ } else {
-+ if (samsung_touchkey->keycode_type == TM2_TOUCHKEY_KEY_MENU)
-+ input_report_key(samsung_touchkey->input_dev,
++ if (touchkey->keycode_type == TM2_TOUCHKEY_KEY_MENU)
++ input_report_key(touchkey->input_dev,
+ KEY_PHONE, 1);
+ else
-+ input_report_key(samsung_touchkey->input_dev,
++ input_report_key(touchkey->input_dev,
+ KEY_BACK, 1);
+ }
-+ input_sync(samsung_touchkey->input_dev);
-+}
-+
-+static irqreturn_t tm2_touchkey_irq_handler(int irq, void *devid)
-+{
-+ struct tm2_touchkey_data *samsung_touchkey = devid;
-+ u32 data;
-+
-+ data = i2c_smbus_read_byte_data(samsung_touchkey->client,
-+ TM2_TOUCHKEY_KEYCODE_REG);
-+
-+ if (data < 0) {
-+ dev_err(&samsung_touchkey->client->dev, "Failed to read i2c data\n");
-+ return IRQ_HANDLED;
-+ }
-+
-+ samsung_touchkey->keycode_type = data & TM2_TOUCHKEY_BIT_KEYCODE;
-+ samsung_touchkey->pressed = !(data & TM2_TOUCHKEY_BIT_PRESS_EV);
-+
-+ if (samsung_touchkey->keycode_type != TM2_TOUCHKEY_KEY_MENU &&
-+ samsung_touchkey->keycode_type != TM2_TOUCHKEY_KEY_BACK)
-+ return IRQ_HANDLED;
-+
-+ schedule_work(&samsung_touchkey->irq_work);
++ input_sync(touchkey->input_dev);
+
+ return IRQ_HANDLED;
+}
+
+static int tm2_touchkey_probe(struct i2c_client *client,
-+ const struct i2c_device_id *id)
-+{
-+ struct tm2_touchkey_data *samsung_touchkey;
++ const struct i2c_device_id *id)
++{
++ struct tm2_touchkey_data *touchkey;
+ int ret;
+
+ ret = i2c_check_functionality(client->adapter,
@@ -228,37 +213,54 @@
+ return -ENODEV;
+ }
+
-+ samsung_touchkey = devm_kzalloc(&client->dev,
-+ sizeof(struct tm2_touchkey_data), GFP_KERNEL);
-+
-+ if (!samsung_touchkey) {
-+ dev_err(&client->dev, "Failed to allocate memory.\n");
++ touchkey = devm_kzalloc(&client->dev, sizeof(*touchkey), GFP_KERNEL);
++ if (!touchkey)
+ return -ENOMEM;
-+ }
-+
-+ samsung_touchkey->client = client;
-+ i2c_set_clientdata(client, samsung_touchkey);
-+ INIT_WORK(&samsung_touchkey->irq_work, tm2_touchkey_irq_work);
-+
-+ /* regulator */
-+ samsung_touchkey->regulator_vcc =
-+ devm_regulator_get(&client->dev, "vcc");
-+ if (IS_ERR(samsung_touchkey->regulator_vcc)) {
-+ dev_err(&client->dev, "Failed to get vcc regulator\n");
-+ return PTR_ERR(samsung_touchkey->regulator_vcc);
-+ }
-+
-+ samsung_touchkey->regulator_vdd =
-+ devm_regulator_get(&client->dev, "vdd");
-+ if (IS_ERR(samsung_touchkey->regulator_vdd)) {
-+ dev_err(&client->dev, "Failed to get vdd regulator\n");
-+ return PTR_ERR(samsung_touchkey->regulator_vcc);
++
++ touchkey->client = client;
++ i2c_set_clientdata(client, touchkey);
++
++ /* regulators */
++ touchkey->regulators[TM2_TOUCHKEY_SUPPLIES_VCC].supply = "vcc";
++ touchkey->regulators[TM2_TOUCHKEY_SUPPLIES_VDD].supply = "vdd";
++ ret = devm_regulator_bulk_get(&client->dev,
++ ARRAY_SIZE(touchkey->regulators),
++ touchkey->regulators);
++ if (ret) {
++ dev_err(&client->dev, "Failed to get regulators\n");
++ return ret;
+ }
+
+ /* power */
-+ ret = tm2_touchkey_power_enable(samsung_touchkey);
++ ret = tm2_touchkey_power_enable(touchkey);
+ if (ret) {
+ dev_err(&client->dev, "Failed to enable power\n");
++ return ret;
++ }
++
++ ret = devm_add_action_or_reset(&client->dev,
++ tm2_touchkey_power_disable, touchkey);
++ if (ret)
++ return ret;
++
++ /* input device */
++ touchkey->input_dev = devm_input_allocate_device(&client->dev);
++ if (!touchkey->input_dev) {
++ dev_err(&client->dev, "Failed to alloc input device\n");
++ return -ENOMEM;
++ }
++ touchkey->input_dev->name = TM2_TOUCHKEY_DEV_NAME;
++ touchkey->input_dev->id.bustype = BUS_I2C;
++
++ set_bit(EV_KEY, touchkey->input_dev->evbit);
++ input_set_capability(touchkey->input_dev, EV_KEY, KEY_PHONE);
++ input_set_capability(touchkey->input_dev, EV_KEY, KEY_BACK);
++
++ input_set_drvdata(touchkey->input_dev, touchkey);
++
++ ret = input_register_device(touchkey->input_dev);
++ if (ret) {
++ dev_err(&client->dev, "Failed to register input device\n");
+ return ret;
+ }
+
@@ -266,44 +268,20 @@
+ ret = devm_request_threaded_irq(&client->dev,
+ client->irq, NULL,
+ tm2_touchkey_irq_handler,
-+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-+ TM2_TOUCHKEY_DEV_NAME,
-+ samsung_touchkey);
++ IRQF_ONESHOT, TM2_TOUCHKEY_DEV_NAME,
++ touchkey);
+ if (ret) {
+ dev_err(&client->dev, "Failed to request threaded irq\n");
+ return ret;
+ }
+
-+ /* input device */
-+ samsung_touchkey->input_dev = devm_input_allocate_device(&client->dev);
-+ if (!samsung_touchkey->input_dev) {
-+ dev_err(&client->dev, "Failed to alloc input device.\n");
-+ return -ENOMEM;
-+ }
-+ samsung_touchkey->input_dev->name = TM2_TOUCHKEY_DEV_NAME;
-+ samsung_touchkey->input_dev->id.bustype = BUS_I2C;
-+ samsung_touchkey->input_dev->dev.parent = &client->dev;
-+
-+ set_bit(EV_KEY, samsung_touchkey->input_dev->evbit);
-+ set_bit(KEY_PHONE, samsung_touchkey->input_dev->keybit);
-+ set_bit(KEY_BACK, samsung_touchkey->input_dev->keybit);
-+ input_set_drvdata(samsung_touchkey->input_dev, samsung_touchkey);
-+
-+ ret = input_register_device(samsung_touchkey->input_dev);
-+ if (ret) {
-+ dev_err(&client->dev, "Failed to register input device.\n");
-+ return ret;
-+ }
-+
+ /* led device */
-+ samsung_touchkey->led_dev.name = TM2_TOUCHKEY_DEV_NAME;
-+ samsung_touchkey->led_dev.brightness = LED_FULL;
-+ samsung_touchkey->led_dev.max_brightness = LED_FULL;
-+ samsung_touchkey->led_dev.brightness_set =
-+ tm2_touchkey_led_brightness_set;
-+
-+ ret = devm_led_classdev_register(&client->dev,
-+ &samsung_touchkey->led_dev);
++ touchkey->led_dev.name = TM2_TOUCHKEY_DEV_NAME;
++ touchkey->led_dev.brightness = LED_FULL;
++ touchkey->led_dev.max_brightness = LED_FULL;
++ touchkey->led_dev.brightness_set = tm2_touchkey_led_brightness_set;
++
++ ret = devm_led_classdev_register(&client->dev, &touchkey->led_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to register touchkey led\n");
+ return ret;
@@ -312,38 +290,23 @@
+ return 0;
+}
+
-+static void tm2_touchkey_shutdown(struct i2c_client *client)
-+{
-+ struct tm2_touchkey_data *samsung_touchkey =
-+ i2c_get_clientdata(client);
++static int __maybe_unused tm2_touchkey_suspend(struct device *dev)
++{
++ struct tm2_touchkey_data *touchkey = dev_get_drvdata(dev);
++
++ disable_irq(touchkey->client->irq);
++ tm2_touchkey_power_disable(touchkey);
++
++ return 0;
++}
++
++static int __maybe_unused tm2_touchkey_resume(struct device *dev)
++{
++ struct tm2_touchkey_data *touchkey = dev_get_drvdata(dev);
+ int ret;
+
-+ disable_irq(client->irq);
-+ ret = tm2_touchkey_power_disable(samsung_touchkey);
-+ if (ret)
-+ dev_err(&client->dev, "Failed to disable power\n");
-+}
-+
-+static int tm2_touchkey_suspend(struct device *dev)
-+{
-+ struct tm2_touchkey_data *samsung_touchkey = dev_get_drvdata(dev);
-+ int ret;
-+
-+ disable_irq(samsung_touchkey->client->irq);
-+ ret = tm2_touchkey_power_disable(samsung_touchkey);
-+ if (ret)
-+ dev_err(dev, "Failed to disable power\n");
-+
-+ return ret;
-+}
-+
-+static int tm2_touchkey_resume(struct device *dev)
-+{
-+ struct tm2_touchkey_data *samsung_touchkey = dev_get_drvdata(dev);
-+ int ret;
-+
-+ enable_irq(samsung_touchkey->client->irq);
-+ ret = tm2_touchkey_power_enable(samsung_touchkey);
++ enable_irq(touchkey->client->irq);
++ ret = tm2_touchkey_power_enable(touchkey);
+ if (ret)
+ dev_err(dev, "Failed to enable power\n");
+
@@ -357,11 +320,13 @@
+ {TM2_TOUCHKEY_DEV_NAME, 0},
+ {},
+};
++MODULE_DEVICE_TABLE(i2c, tm2_touchkey_id_table);
+
+static const struct of_device_id tm2_touchkey_of_match[] = {
-+ {.compatible = "samsung,tm2-touchkey",},
++ {.compatible = "cypress,tm2-touchkey",},
+ {},
+};
++MODULE_DEVICE_TABLE(of, tm2_touchkey_of_match);
+
+static struct i2c_driver tm2_touchkey_driver = {
+ .driver = {
@@ -370,7 +335,6 @@
+ .of_match_table = of_match_ptr(tm2_touchkey_of_match),
+ },
+ .probe = tm2_touchkey_probe,
-+ .shutdown = tm2_touchkey_shutdown,
+ .id_table = tm2_touchkey_id_table,
+};
+