The MELPAS MCS5000 can use to touchkey controller. This is patch to support touchkey at original MCS5000 touchscreen driver. Signed-off-by: HeungJun Kim <riverful.kim@xxxxxxxxxxx> Signed-off-by: Joonyoung Shim <jy0922.shim@xxxxxxxxxxx> Reviewed-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> --- drivers/input/touchscreen/Kconfig | 6 +- drivers/input/touchscreen/mcs5000_ts.c | 245 ++++++++++++++++++++++++-------- include/linux/i2c/mcs5000_ts.h | 17 ++- 3 files changed, 201 insertions(+), 67 deletions(-) diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index cc47198..d569e87 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -202,11 +202,11 @@ config TOUCHSCREEN_WACOM_W8001 module will be called wacom_w8001. config TOUCHSCREEN_MCS5000 - tristate "MELFAS MCS-5000 touchscreen" + tristate "MELFAS MCS5000 touchscreen/touchkey" depends on I2C help - Say Y here if you have the MELFAS MCS-5000 touchscreen controller - chip in your system. + Say Y here if you have the MELFAS MCS5000 touchscreen/touchkey + controller chip in your system. If unsure, say N. diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c index ce8ab02..87842e3 100644 --- a/drivers/input/touchscreen/mcs5000_ts.c +++ b/drivers/input/touchscreen/mcs5000_ts.c @@ -1,10 +1,9 @@ /* - * mcs5000_ts.c - Touchscreen driver for MELFAS MCS-5000 controller + * mcs5000_ts.c - Touchscreen/Touchkey driver for MELFAS MCS5000 controller * - * Copyright (C) 2009 Samsung Electronics Co.Ltd + * Copyright (C) 2009 - 2010 Samsung Electronics Co.Ltd * Author: Joonyoung Shim <jy0922.shim@xxxxxxxxxxx> - * - * Based on wm97xx-core.c + * Author: HeungJun Kim <riverful.kim@xxxxxxxxxxx> * * 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 @@ -22,7 +21,7 @@ #include <linux/irq.h> #include <linux/slab.h> -/* Registers */ +/* Touchscreen Registers */ #define MCS5000_TS_STATUS 0x00 #define STATUS_OFFSET 0 #define STATUS_NO (0 << STATUS_OFFSET) @@ -92,6 +91,21 @@ #define MCS5000_MAX_XC 0x3ff #define MCS5000_MAX_YC 0x3ff +/* Touchkey Registers */ +#define MCS5000_TK_LED_ONOFF 0x01 +#define MCS5000_TK_LED_DIMMING 0x02 +#define MCS5000_TK_RESERVED1 0x03 +#define MCS5000_TK_VALUE_STATUS 0x04 +#define MCS5000_TK_RESERVED2 0x05 +#define MCS5000_TK_HW_VERSION 0x06 +#define MCS5000_TK_FW_VERSION 0x0A +#define MCS5000_TK_MI_VERSION 0x0B + +enum mcs5000_type { + MCS5000_TOUCHSCREEN, + MCS5000_TOUCHKEY, +}; + enum mcs5000_ts_read_offset { READ_INPUT_INFO, READ_X_POS_UPPER, @@ -102,15 +116,59 @@ enum mcs5000_ts_read_offset { }; /* Each client has this additional data */ -struct mcs5000_ts_data { +struct mcs5000_data { struct i2c_client *client; struct input_dev *input_dev; - const struct mcs5000_ts_platform_data *platform_data; + const struct mcs5000_platform_data *pdata; + unsigned int type; + unsigned int key_code; }; -static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id) +static unsigned int mcs5000_get_type(struct mcs5000_data *data) +{ + return data->type; +} + +static void mcs5000_tk_interrupt(struct mcs5000_data *data) +{ + const struct mcs5000_platform_data *pdata = data->pdata; + struct i2c_client *client = data->client; + struct input_dev *input = data->input_dev; + unsigned int key_val; + unsigned int key_code; + unsigned int pressed; + int val; + int i; + + val = i2c_smbus_read_byte_data(client, MCS5000_TK_VALUE_STATUS); + if (val < 0) { + dev_err(&client->dev, "%s, err[%d]\n", __func__, val); + return; + } + + key_val = val & 0x7f; + pressed = val >> 7; + + if (pressed) { + for (i = 0; i < pdata->keymap_size; i++) { + if (MCS5000_KEY_VAL(pdata->keymap[i]) == key_val) { + key_code = MCS5000_KEY_CODE(pdata->keymap[i]); + data->key_code = key_code; + break; + } + } + } else + key_code = data->key_code; + + input_report_key(input, key_code, pressed ? 1 : 0); + input_sync(input); + + dev_dbg(&client->dev, "key %d %d %s\n", key_val, key_code, + pressed ? "pressed" : "released"); +} + +static void mcs5000_ts_interrupt(struct mcs5000_data *data) { - struct mcs5000_ts_data *data = dev_id; struct i2c_client *client = data->client; u8 buffer[READ_BLOCK_SIZE]; int err; @@ -121,7 +179,7 @@ static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id) READ_BLOCK_SIZE, buffer); if (err < 0) { dev_err(&client->dev, "%s, err[%d]\n", __func__, err); - goto out; + return; } switch (buffer[READ_INPUT_INFO]) { @@ -157,15 +215,29 @@ static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id) buffer[READ_INPUT_INFO]); break; } +} + +static irqreturn_t mcs5000_interrupt(int irq, void *dev_id) +{ + struct mcs5000_data *data = dev_id; + + switch (mcs5000_get_type(data)) { + case MCS5000_TOUCHSCREEN: + mcs5000_ts_interrupt(data); + break; + case MCS5000_TOUCHKEY: + mcs5000_tk_interrupt(data); + break; + default: + break; + } - out: return IRQ_HANDLED; } -static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data) +static void mcs5000_ts_init(struct mcs5000_data *data) { - const struct mcs5000_ts_platform_data *platform_data = - data->platform_data; + const struct mcs5000_platform_data *pdata = data->pdata; struct i2c_client *client = data->client; /* Touch reset & sleep mode */ @@ -174,30 +246,32 @@ static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data) /* Touch size */ i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_UPPER, - platform_data->x_size >> 8); + pdata->x_size >> 8); i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_LOWER, - platform_data->x_size & 0xff); + pdata->x_size & 0xff); i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_UPPER, - platform_data->y_size >> 8); + pdata->y_size >> 8); i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_LOWER, - platform_data->y_size & 0xff); + pdata->y_size & 0xff); /* Touch active mode & 80 report rate */ i2c_smbus_write_byte_data(data->client, MCS5000_TS_OP_MODE, OP_MODE_ACTIVE | REPORT_RATE_80); } -static int __devinit mcs5000_ts_probe(struct i2c_client *client, +static int __devinit mcs5000_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct mcs5000_ts_data *data; + struct mcs5000_data *data; struct input_dev *input_dev; + unsigned long irq_flags = 0; int ret; + int i; if (!client->dev.platform_data) return -EINVAL; - data = kzalloc(sizeof(struct mcs5000_ts_data), GFP_KERNEL); + data = kzalloc(sizeof(struct mcs5000_data), GFP_KERNEL); input_dev = input_allocate_device(); if (!data || !input_dev) { dev_err(&client->dev, "Failed to allocate memory\n"); @@ -207,25 +281,44 @@ static int __devinit mcs5000_ts_probe(struct i2c_client *client, data->client = client; data->input_dev = input_dev; - data->platform_data = client->dev.platform_data; + data->pdata = client->dev.platform_data; + data->type = id->driver_data; + + switch (mcs5000_get_type(data)) { + case MCS5000_TOUCHSCREEN: + input_dev->name = "MELPAS MCS5000 Touchscreen"; + input_dev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, 0, MCS5000_MAX_XC, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MCS5000_MAX_YC, 0, 0); + + irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT; + break; + case MCS5000_TOUCHKEY: + input_dev->name = "MELPAS MCS5000 Touchkey"; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);; + + for (i = 0; i < data->pdata->keymap_size; i++) + __set_bit(MCS5000_KEY_CODE(data->pdata->keymap[i]), + input_dev->keybit); + + irq_flags = IRQF_TRIGGER_FALLING; + break; + default: + break; + } - input_dev->name = "MELPAS MCS-5000 Touchscreen"; input_dev->id.bustype = BUS_I2C; input_dev->dev.parent = &client->dev; - __set_bit(EV_ABS, input_dev->evbit); - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(BTN_TOUCH, input_dev->keybit); - input_set_abs_params(input_dev, ABS_X, 0, MCS5000_MAX_XC, 0, 0); - input_set_abs_params(input_dev, ABS_Y, 0, MCS5000_MAX_YC, 0, 0); - input_set_drvdata(input_dev, data); - if (data->platform_data->cfg_pin) - data->platform_data->cfg_pin(); + if (data->pdata->cfg_pin) + data->pdata->cfg_pin(); - ret = request_threaded_irq(client->irq, NULL, mcs5000_ts_interrupt, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, "mcs5000_ts", data); + ret = request_threaded_irq(client->irq, NULL, mcs5000_interrupt, + irq_flags, client->dev.driver->name, data); if (ret < 0) { dev_err(&client->dev, "Failed to register interrupt\n"); @@ -236,7 +329,16 @@ static int __devinit mcs5000_ts_probe(struct i2c_client *client, if (ret < 0) goto err_free_irq; - mcs5000_ts_phys_init(data); + switch (mcs5000_get_type(data)) { + case MCS5000_TOUCHSCREEN: + mcs5000_ts_init(data); + break; + case MCS5000_TOUCHKEY: + break; + default: + break; + } + i2c_set_clientdata(client, data); return 0; @@ -249,9 +351,9 @@ err_free_mem: return ret; } -static int __devexit mcs5000_ts_remove(struct i2c_client *client) +static int __devexit mcs5000_remove(struct i2c_client *client) { - struct mcs5000_ts_data *data = i2c_get_clientdata(client); + struct mcs5000_data *data = i2c_get_clientdata(client); free_irq(client->irq, data); input_unregister_device(data->input_dev); @@ -262,58 +364,79 @@ static int __devexit mcs5000_ts_remove(struct i2c_client *client) } #ifdef CONFIG_PM -static int mcs5000_ts_suspend(struct i2c_client *client, pm_message_t mesg) +static int mcs5000_suspend(struct i2c_client *client, pm_message_t mesg) { - /* Touch sleep mode */ - i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, OP_MODE_SLEEP); + struct mcs5000_data *data = i2c_get_clientdata(client); + + switch (mcs5000_get_type(data)) { + case MCS5000_TOUCHSCREEN: + /* Touch sleep mode */ + i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, + OP_MODE_SLEEP); + break; + case MCS5000_TOUCHKEY: + break; + default: + break; + } return 0; } -static int mcs5000_ts_resume(struct i2c_client *client) +static int mcs5000_resume(struct i2c_client *client) { - struct mcs5000_ts_data *data = i2c_get_clientdata(client); + struct mcs5000_data *data = i2c_get_clientdata(client); - mcs5000_ts_phys_init(data); + switch (mcs5000_get_type(data)) { + case MCS5000_TOUCHSCREEN: + mcs5000_ts_init(data); + break; + case MCS5000_TOUCHKEY: + break; + default: + break; + } return 0; } #else -#define mcs5000_ts_suspend NULL -#define mcs5000_ts_resume NULL +#define mcs5000_suspend NULL +#define mcs5000_resume NULL #endif -static const struct i2c_device_id mcs5000_ts_id[] = { - { "mcs5000_ts", 0 }, +static const struct i2c_device_id mcs5000_id[] = { + { "mcs5000_ts", MCS5000_TOUCHSCREEN }, + { "mcs5000_tk", MCS5000_TOUCHKEY }, { } }; -MODULE_DEVICE_TABLE(i2c, mcs5000_ts_id); +MODULE_DEVICE_TABLE(i2c, mcs5000_id); -static struct i2c_driver mcs5000_ts_driver = { - .probe = mcs5000_ts_probe, - .remove = __devexit_p(mcs5000_ts_remove), - .suspend = mcs5000_ts_suspend, - .resume = mcs5000_ts_resume, +static struct i2c_driver mcs5000_driver = { + .probe = mcs5000_probe, + .remove = __devexit_p(mcs5000_remove), + .suspend = mcs5000_suspend, + .resume = mcs5000_resume, .driver = { - .name = "mcs5000_ts", + .name = "mcs5000", }, - .id_table = mcs5000_ts_id, + .id_table = mcs5000_id, }; -static int __init mcs5000_ts_init(void) +static int __init mcs5000_init(void) { - return i2c_add_driver(&mcs5000_ts_driver); + return i2c_add_driver(&mcs5000_driver); } -static void __exit mcs5000_ts_exit(void) +static void __exit mcs5000_exit(void) { - i2c_del_driver(&mcs5000_ts_driver); + i2c_del_driver(&mcs5000_driver); } -module_init(mcs5000_ts_init); -module_exit(mcs5000_ts_exit); +module_init(mcs5000_init); +module_exit(mcs5000_exit); /* Module information */ MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@xxxxxxxxxxx>"); -MODULE_DESCRIPTION("Touchscreen driver for MELFAS MCS-5000 controller"); +MODULE_AUTHOR("HeungJun Kim <riverful.kim@xxxxxxxxxxx>"); +MODULE_DESCRIPTION("Touchscreen/Touchkey driver for MELFAS MCS5000 controller"); MODULE_LICENSE("GPL"); diff --git a/include/linux/i2c/mcs5000_ts.h b/include/linux/i2c/mcs5000_ts.h index 5a117b5..578e3e5 100644 --- a/include/linux/i2c/mcs5000_ts.h +++ b/include/linux/i2c/mcs5000_ts.h @@ -1,8 +1,9 @@ /* * mcs5000_ts.h * - * Copyright (C) 2009 Samsung Electronics Co.Ltd + * Copyright (C) 2009 - 2010 Samsung Electronics Co.Ltd * Author: Joonyoung Shim <jy0922.shim@xxxxxxxxxxx> + * Author: HeungJun Kim <riverful.kim@xxxxxxxxxxx> * * 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 @@ -14,11 +15,21 @@ #ifndef __LINUX_MCS5000_TS_H #define __LINUX_MCS5000_TS_H -/* platform data for the MELFAS MCS-5000 touchscreen driver */ -struct mcs5000_ts_platform_data { +#define MCS5000_KEY_MAP(v, c) ((((v) & 0xff) << 16) | ((c) & 0xffff)) +#define MCS5000_KEY_VAL(v) (((v) >> 16) & 0xff) +#define MCS5000_KEY_CODE(v) ((v) & 0xffff) + +/* platform data for the MELFAS MCS5000 touchscreen/touchkey driver */ +struct mcs5000_platform_data { void (*cfg_pin)(void); + + /* touchscreen */ int x_size; int y_size; + + /* touchkey */ + const uint32_t *keymap; + unsigned int keymap_size; }; #endif /* __LINUX_MCS5000_TS_H */ -- 1.7.0.4 -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html