From 312e8e8a9e2471b0ada7366497fffb3ff1a40e2c Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Sun, 4 Jul 2010 01:21:25 -0700 Subject: [PATCH] Input: mcs - Add MCS touchkey driver This adds support for MELPAS MCS5000/MSC5080 touch key controllers. Signed-off-by: Joonyoung Shim Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 12 ++ drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/mcs_touchkey.c | 239 +++++++++++++++++++++++++ drivers/input/touchscreen/mcs5000_ts.c | 6 +- include/linux/i2c/mcs.h | 34 ++++ include/linux/i2c/mcs5000_ts.h | 24 --- 6 files changed, 289 insertions(+), 27 deletions(-) create mode 100644 drivers/input/keyboard/mcs_touchkey.c create mode 100644 include/linux/i2c/mcs.h delete mode 100644 include/linux/i2c/mcs5000_ts.h diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index d8fa5d724c5..c7480934cee 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -297,6 +297,18 @@ config KEYBOARD_MAX7359 To compile this driver as a module, choose M here: the module will be called max7359_keypad. +config KEYBOARD_MCS + tristate "MELFAS MCS Touchkey" + depends on I2C + help + Say Y here if you have the MELFAS MCS5000/5080 touchkey controller + chip in your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mcs_touchkey. + config KEYBOARD_IMX tristate "IMX keypad support" depends on ARCH_MXC diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 4596d0c6f92..0a16674ed3d 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o +obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o diff --git a/drivers/input/keyboard/mcs_touchkey.c b/drivers/input/keyboard/mcs_touchkey.c new file mode 100644 index 00000000000..63b849d7e90 --- /dev/null +++ b/drivers/input/keyboard/mcs_touchkey.c @@ -0,0 +1,239 @@ +/* + * mcs_touchkey.c - Touchkey driver for MELFAS MCS5000/5080 controller + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Author: HeungJun Kim + * Author: Joonyoung Shim + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* MCS5000 Touchkey */ +#define MCS5000_TOUCHKEY_STATUS 0x04 +#define MCS5000_TOUCHKEY_STATUS_PRESS 7 +#define MCS5000_TOUCHKEY_FW 0x0a +#define MCS5000_TOUCHKEY_BASE_VAL 0x61 + +/* MCS5080 Touchkey */ +#define MCS5080_TOUCHKEY_STATUS 0x00 +#define MCS5080_TOUCHKEY_STATUS_PRESS 3 +#define MCS5080_TOUCHKEY_FW 0x01 +#define MCS5080_TOUCHKEY_BASE_VAL 0x1 + +enum mcs_touchkey_type { + MCS5000_TOUCHKEY, + MCS5080_TOUCHKEY, +}; + +struct mcs_touchkey_chip { + unsigned int status_reg; + unsigned int pressbit; + unsigned int press_invert; + unsigned int baseval; +}; + +struct mcs_touchkey_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct mcs_touchkey_chip chip; + unsigned int key_code; + unsigned int key_val; + unsigned short keycodes[]; +}; + +static irqreturn_t mcs_touchkey_interrupt(int irq, void *dev_id) +{ + struct mcs_touchkey_data *data = dev_id; + struct mcs_touchkey_chip *chip = &data->chip; + struct i2c_client *client = data->client; + struct input_dev *input = data->input_dev; + unsigned int key_val; + unsigned int pressed; + int val; + + val = i2c_smbus_read_byte_data(client, chip->status_reg); + if (val < 0) { + dev_err(&client->dev, "i2c read error [%d]\n", val); + goto out; + } + + pressed = (val & (1 << chip->pressbit)) >> chip->pressbit; + if (chip->press_invert) + pressed ^= chip->press_invert; + + /* key_val is 0 when released, so we should use key_val of press. */ + if (pressed) { + key_val = val & (0xff >> (8 - chip->pressbit)); + if (!key_val) + goto out; + key_val -= chip->baseval; + data->key_code = data->keycodes[key_val]; + data->key_val = key_val; + } + + input_event(input, EV_MSC, MSC_SCAN, data->key_val); + input_report_key(input, data->key_code, pressed); + input_sync(input); + + dev_dbg(&client->dev, "key %d %d %s\n", data->key_val, data->key_code, + pressed ? "pressed" : "released"); + + out: + return IRQ_HANDLED; +} + +static int __devinit mcs_touchkey_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct mcs_platform_data *pdata; + struct mcs_touchkey_data *data; + struct input_dev *input_dev; + unsigned int fw_reg; + int fw_ver; + int error; + int i; + + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->dev, "no platform data defined\n"); + return -EINVAL; + } + + data = kzalloc(sizeof(struct mcs_touchkey_data) + + sizeof(data->keycodes[0]) * (pdata->key_maxval + 1), + GFP_KERNEL); + input_dev = input_allocate_device(); + if (!data || !input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + data->client = client; + data->input_dev = input_dev; + + if (id->driver_data == MCS5000_TOUCHKEY) { + data->chip.status_reg = MCS5000_TOUCHKEY_STATUS; + data->chip.pressbit = MCS5000_TOUCHKEY_STATUS_PRESS; + data->chip.baseval = MCS5000_TOUCHKEY_BASE_VAL; + fw_reg = MCS5000_TOUCHKEY_FW; + } else { + data->chip.status_reg = MCS5080_TOUCHKEY_STATUS; + data->chip.pressbit = MCS5080_TOUCHKEY_STATUS_PRESS; + data->chip.press_invert = 1; + data->chip.baseval = MCS5080_TOUCHKEY_BASE_VAL; + fw_reg = MCS5080_TOUCHKEY_FW; + } + + fw_ver = i2c_smbus_read_byte_data(client, fw_reg); + if (fw_ver < 0) { + error = fw_ver; + dev_err(&client->dev, "i2c read error[%d]\n", error); + goto err_free_mem; + } + dev_info(&client->dev, "Firmware version: %d\n", fw_ver); + + input_dev->name = "MELPAS MCS Touchkey"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY); + if (!pdata->no_autorepeat) + input_dev->evbit[0] |= BIT_MASK(EV_REP); + input_dev->keycode = data->keycodes; + input_dev->keycodesize = sizeof(data->keycodes[0]); + input_dev->keycodemax = pdata->key_maxval + 1; + + for (i = 0; i < pdata->keymap_size; i++) { + unsigned int val = MCS_KEY_VAL(pdata->keymap[i]); + unsigned int code = MCS_KEY_CODE(pdata->keymap[i]); + + data->keycodes[val] = code; + __set_bit(code, input_dev->keybit); + } + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + input_set_drvdata(input_dev, data); + + if (pdata->cfg_pin) + pdata->cfg_pin(); + + error = request_threaded_irq(client->irq, NULL, mcs_touchkey_interrupt, + IRQF_TRIGGER_FALLING, client->dev.driver->name, data); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_mem; + } + + error = input_register_device(input_dev); + if (error) + goto err_free_irq; + + i2c_set_clientdata(client, data); + return 0; + +err_free_irq: + free_irq(client->irq, data); +err_free_mem: + input_free_device(input_dev); + kfree(data); + return error; +} + +static int __devexit mcs_touchkey_remove(struct i2c_client *client) +{ + struct mcs_touchkey_data *data = i2c_get_clientdata(client); + + free_irq(client->irq, data); + input_unregister_device(data->input_dev); + kfree(data); + + return 0; +} + +static const struct i2c_device_id mcs_touchkey_id[] = { + { "mcs5000_touchkey", MCS5000_TOUCHKEY }, + { "mcs5080_touchkey", MCS5080_TOUCHKEY }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcs_touchkey_id); + +static struct i2c_driver mcs_touchkey_driver = { + .driver = { + .name = "mcs_touchkey", + .owner = THIS_MODULE, + }, + .probe = mcs_touchkey_probe, + .remove = __devexit_p(mcs_touchkey_remove), + .id_table = mcs_touchkey_id, +}; + +static int __init mcs_touchkey_init(void) +{ + return i2c_add_driver(&mcs_touchkey_driver); +} + +static void __exit mcs_touchkey_exit(void) +{ + i2c_del_driver(&mcs_touchkey_driver); +} + +module_init(mcs_touchkey_init); +module_exit(mcs_touchkey_exit); + +/* Module information */ +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_AUTHOR("HeungJun Kim "); +MODULE_DESCRIPTION("Touchkey driver for MELFAS MCS5000/5080 controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c index 1fb0c2f06a4..6ee9940aaf5 100644 --- a/drivers/input/touchscreen/mcs5000_ts.c +++ b/drivers/input/touchscreen/mcs5000_ts.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include @@ -105,7 +105,7 @@ enum mcs5000_ts_read_offset { struct mcs5000_ts_data { struct i2c_client *client; struct input_dev *input_dev; - const struct mcs5000_ts_platform_data *platform_data; + const struct mcs_platform_data *platform_data; }; static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id) @@ -164,7 +164,7 @@ static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id) static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data) { - const struct mcs5000_ts_platform_data *platform_data = + const struct mcs_platform_data *platform_data = data->platform_data; struct i2c_client *client = data->client; diff --git a/include/linux/i2c/mcs.h b/include/linux/i2c/mcs.h new file mode 100644 index 00000000000..725ae7c313f --- /dev/null +++ b/include/linux/i2c/mcs.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2009 - 2010 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + * Author: HeungJun Kim + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __LINUX_MCS_H +#define __LINUX_MCS_H + +#define MCS_KEY_MAP(v, c) ((((v) & 0xff) << 16) | ((c) & 0xffff)) +#define MCS_KEY_VAL(v) (((v) >> 16) & 0xff) +#define MCS_KEY_CODE(v) ((v) & 0xffff) + +struct mcs_platform_data { + void (*cfg_pin)(void); + + /* touchscreen */ + unsigned int x_size; + unsigned int y_size; + + /* touchkey */ + const u32 *keymap; + unsigned int keymap_size; + unsigned int key_maxval; + bool no_autorepeat; +}; + +#endif /* __LINUX_MCS_H */ diff --git a/include/linux/i2c/mcs5000_ts.h b/include/linux/i2c/mcs5000_ts.h deleted file mode 100644 index 5a117b5ca15..00000000000 --- a/include/linux/i2c/mcs5000_ts.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * mcs5000_ts.h - * - * Copyright (C) 2009 Samsung Electronics Co.Ltd - * Author: Joonyoung Shim - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#ifndef __LINUX_MCS5000_TS_H -#define __LINUX_MCS5000_TS_H - -/* platform data for the MELFAS MCS-5000 touchscreen driver */ -struct mcs5000_ts_platform_data { - void (*cfg_pin)(void); - int x_size; - int y_size; -}; - -#endif /* __LINUX_MCS5000_TS_H */ -- 2.46.0