Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Input: mcs - Add MCS touchkey driver

This adds support for MELPAS MCS5000/MSC5080 touch key controllers.

Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>

authored by

Joonyoung Shim and committed by
Dmitry Torokhov
312e8e8a 866d7d7b

+289 -27
+12
drivers/input/keyboard/Kconfig
··· 297 297 To compile this driver as a module, choose M here: the 298 298 module will be called max7359_keypad. 299 299 300 + config KEYBOARD_MCS 301 + tristate "MELFAS MCS Touchkey" 302 + depends on I2C 303 + help 304 + Say Y here if you have the MELFAS MCS5000/5080 touchkey controller 305 + chip in your system. 306 + 307 + If unsure, say N. 308 + 309 + To compile this driver as a module, choose M here: the 310 + module will be called mcs_touchkey. 311 + 300 312 config KEYBOARD_IMX 301 313 tristate "IMX keypad support" 302 314 depends on ARCH_MXC
+1
drivers/input/keyboard/Makefile
··· 26 26 obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o 27 27 obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o 28 28 obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o 29 + obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o 29 30 obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o 30 31 obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o 31 32 obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
+239
drivers/input/keyboard/mcs_touchkey.c
··· 1 + /* 2 + * mcs_touchkey.c - Touchkey driver for MELFAS MCS5000/5080 controller 3 + * 4 + * Copyright (C) 2010 Samsung Electronics Co.Ltd 5 + * Author: HeungJun Kim <riverful.kim@samsung.com> 6 + * Author: Joonyoung Shim <jy0922.shim@samsung.com> 7 + * 8 + * This program is free software; you can redistribute it and/or modify it 9 + * under the terms of the GNU General Public License as published by the 10 + * Free Software Foundation; either version 2 of the License, or (at your 11 + * option) any later version. 12 + */ 13 + 14 + #include <linux/module.h> 15 + #include <linux/init.h> 16 + #include <linux/i2c.h> 17 + #include <linux/i2c/mcs.h> 18 + #include <linux/interrupt.h> 19 + #include <linux/input.h> 20 + #include <linux/irq.h> 21 + #include <linux/slab.h> 22 + 23 + /* MCS5000 Touchkey */ 24 + #define MCS5000_TOUCHKEY_STATUS 0x04 25 + #define MCS5000_TOUCHKEY_STATUS_PRESS 7 26 + #define MCS5000_TOUCHKEY_FW 0x0a 27 + #define MCS5000_TOUCHKEY_BASE_VAL 0x61 28 + 29 + /* MCS5080 Touchkey */ 30 + #define MCS5080_TOUCHKEY_STATUS 0x00 31 + #define MCS5080_TOUCHKEY_STATUS_PRESS 3 32 + #define MCS5080_TOUCHKEY_FW 0x01 33 + #define MCS5080_TOUCHKEY_BASE_VAL 0x1 34 + 35 + enum mcs_touchkey_type { 36 + MCS5000_TOUCHKEY, 37 + MCS5080_TOUCHKEY, 38 + }; 39 + 40 + struct mcs_touchkey_chip { 41 + unsigned int status_reg; 42 + unsigned int pressbit; 43 + unsigned int press_invert; 44 + unsigned int baseval; 45 + }; 46 + 47 + struct mcs_touchkey_data { 48 + struct i2c_client *client; 49 + struct input_dev *input_dev; 50 + struct mcs_touchkey_chip chip; 51 + unsigned int key_code; 52 + unsigned int key_val; 53 + unsigned short keycodes[]; 54 + }; 55 + 56 + static irqreturn_t mcs_touchkey_interrupt(int irq, void *dev_id) 57 + { 58 + struct mcs_touchkey_data *data = dev_id; 59 + struct mcs_touchkey_chip *chip = &data->chip; 60 + struct i2c_client *client = data->client; 61 + struct input_dev *input = data->input_dev; 62 + unsigned int key_val; 63 + unsigned int pressed; 64 + int val; 65 + 66 + val = i2c_smbus_read_byte_data(client, chip->status_reg); 67 + if (val < 0) { 68 + dev_err(&client->dev, "i2c read error [%d]\n", val); 69 + goto out; 70 + } 71 + 72 + pressed = (val & (1 << chip->pressbit)) >> chip->pressbit; 73 + if (chip->press_invert) 74 + pressed ^= chip->press_invert; 75 + 76 + /* key_val is 0 when released, so we should use key_val of press. */ 77 + if (pressed) { 78 + key_val = val & (0xff >> (8 - chip->pressbit)); 79 + if (!key_val) 80 + goto out; 81 + key_val -= chip->baseval; 82 + data->key_code = data->keycodes[key_val]; 83 + data->key_val = key_val; 84 + } 85 + 86 + input_event(input, EV_MSC, MSC_SCAN, data->key_val); 87 + input_report_key(input, data->key_code, pressed); 88 + input_sync(input); 89 + 90 + dev_dbg(&client->dev, "key %d %d %s\n", data->key_val, data->key_code, 91 + pressed ? "pressed" : "released"); 92 + 93 + out: 94 + return IRQ_HANDLED; 95 + } 96 + 97 + static int __devinit mcs_touchkey_probe(struct i2c_client *client, 98 + const struct i2c_device_id *id) 99 + { 100 + const struct mcs_platform_data *pdata; 101 + struct mcs_touchkey_data *data; 102 + struct input_dev *input_dev; 103 + unsigned int fw_reg; 104 + int fw_ver; 105 + int error; 106 + int i; 107 + 108 + pdata = client->dev.platform_data; 109 + if (!pdata) { 110 + dev_err(&client->dev, "no platform data defined\n"); 111 + return -EINVAL; 112 + } 113 + 114 + data = kzalloc(sizeof(struct mcs_touchkey_data) + 115 + sizeof(data->keycodes[0]) * (pdata->key_maxval + 1), 116 + GFP_KERNEL); 117 + input_dev = input_allocate_device(); 118 + if (!data || !input_dev) { 119 + dev_err(&client->dev, "Failed to allocate memory\n"); 120 + error = -ENOMEM; 121 + goto err_free_mem; 122 + } 123 + 124 + data->client = client; 125 + data->input_dev = input_dev; 126 + 127 + if (id->driver_data == MCS5000_TOUCHKEY) { 128 + data->chip.status_reg = MCS5000_TOUCHKEY_STATUS; 129 + data->chip.pressbit = MCS5000_TOUCHKEY_STATUS_PRESS; 130 + data->chip.baseval = MCS5000_TOUCHKEY_BASE_VAL; 131 + fw_reg = MCS5000_TOUCHKEY_FW; 132 + } else { 133 + data->chip.status_reg = MCS5080_TOUCHKEY_STATUS; 134 + data->chip.pressbit = MCS5080_TOUCHKEY_STATUS_PRESS; 135 + data->chip.press_invert = 1; 136 + data->chip.baseval = MCS5080_TOUCHKEY_BASE_VAL; 137 + fw_reg = MCS5080_TOUCHKEY_FW; 138 + } 139 + 140 + fw_ver = i2c_smbus_read_byte_data(client, fw_reg); 141 + if (fw_ver < 0) { 142 + error = fw_ver; 143 + dev_err(&client->dev, "i2c read error[%d]\n", error); 144 + goto err_free_mem; 145 + } 146 + dev_info(&client->dev, "Firmware version: %d\n", fw_ver); 147 + 148 + input_dev->name = "MELPAS MCS Touchkey"; 149 + input_dev->id.bustype = BUS_I2C; 150 + input_dev->dev.parent = &client->dev; 151 + input_dev->evbit[0] = BIT_MASK(EV_KEY); 152 + if (!pdata->no_autorepeat) 153 + input_dev->evbit[0] |= BIT_MASK(EV_REP); 154 + input_dev->keycode = data->keycodes; 155 + input_dev->keycodesize = sizeof(data->keycodes[0]); 156 + input_dev->keycodemax = pdata->key_maxval + 1; 157 + 158 + for (i = 0; i < pdata->keymap_size; i++) { 159 + unsigned int val = MCS_KEY_VAL(pdata->keymap[i]); 160 + unsigned int code = MCS_KEY_CODE(pdata->keymap[i]); 161 + 162 + data->keycodes[val] = code; 163 + __set_bit(code, input_dev->keybit); 164 + } 165 + 166 + input_set_capability(input_dev, EV_MSC, MSC_SCAN); 167 + input_set_drvdata(input_dev, data); 168 + 169 + if (pdata->cfg_pin) 170 + pdata->cfg_pin(); 171 + 172 + error = request_threaded_irq(client->irq, NULL, mcs_touchkey_interrupt, 173 + IRQF_TRIGGER_FALLING, client->dev.driver->name, data); 174 + if (error) { 175 + dev_err(&client->dev, "Failed to register interrupt\n"); 176 + goto err_free_mem; 177 + } 178 + 179 + error = input_register_device(input_dev); 180 + if (error) 181 + goto err_free_irq; 182 + 183 + i2c_set_clientdata(client, data); 184 + return 0; 185 + 186 + err_free_irq: 187 + free_irq(client->irq, data); 188 + err_free_mem: 189 + input_free_device(input_dev); 190 + kfree(data); 191 + return error; 192 + } 193 + 194 + static int __devexit mcs_touchkey_remove(struct i2c_client *client) 195 + { 196 + struct mcs_touchkey_data *data = i2c_get_clientdata(client); 197 + 198 + free_irq(client->irq, data); 199 + input_unregister_device(data->input_dev); 200 + kfree(data); 201 + 202 + return 0; 203 + } 204 + 205 + static const struct i2c_device_id mcs_touchkey_id[] = { 206 + { "mcs5000_touchkey", MCS5000_TOUCHKEY }, 207 + { "mcs5080_touchkey", MCS5080_TOUCHKEY }, 208 + { } 209 + }; 210 + MODULE_DEVICE_TABLE(i2c, mcs_touchkey_id); 211 + 212 + static struct i2c_driver mcs_touchkey_driver = { 213 + .driver = { 214 + .name = "mcs_touchkey", 215 + .owner = THIS_MODULE, 216 + }, 217 + .probe = mcs_touchkey_probe, 218 + .remove = __devexit_p(mcs_touchkey_remove), 219 + .id_table = mcs_touchkey_id, 220 + }; 221 + 222 + static int __init mcs_touchkey_init(void) 223 + { 224 + return i2c_add_driver(&mcs_touchkey_driver); 225 + } 226 + 227 + static void __exit mcs_touchkey_exit(void) 228 + { 229 + i2c_del_driver(&mcs_touchkey_driver); 230 + } 231 + 232 + module_init(mcs_touchkey_init); 233 + module_exit(mcs_touchkey_exit); 234 + 235 + /* Module information */ 236 + MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); 237 + MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>"); 238 + MODULE_DESCRIPTION("Touchkey driver for MELFAS MCS5000/5080 controller"); 239 + MODULE_LICENSE("GPL");
+3 -3
drivers/input/touchscreen/mcs5000_ts.c
··· 16 16 #include <linux/module.h> 17 17 #include <linux/init.h> 18 18 #include <linux/i2c.h> 19 - #include <linux/i2c/mcs5000_ts.h> 19 + #include <linux/i2c/mcs.h> 20 20 #include <linux/interrupt.h> 21 21 #include <linux/input.h> 22 22 #include <linux/irq.h> ··· 105 105 struct mcs5000_ts_data { 106 106 struct i2c_client *client; 107 107 struct input_dev *input_dev; 108 - const struct mcs5000_ts_platform_data *platform_data; 108 + const struct mcs_platform_data *platform_data; 109 109 }; 110 110 111 111 static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id) ··· 164 164 165 165 static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data) 166 166 { 167 - const struct mcs5000_ts_platform_data *platform_data = 167 + const struct mcs_platform_data *platform_data = 168 168 data->platform_data; 169 169 struct i2c_client *client = data->client; 170 170
+34
include/linux/i2c/mcs.h
··· 1 + /* 2 + * Copyright (C) 2009 - 2010 Samsung Electronics Co.Ltd 3 + * Author: Joonyoung Shim <jy0922.shim@samsung.com> 4 + * Author: HeungJun Kim <riverful.kim@samsung.com> 5 + * 6 + * This program is free software; you can redistribute it and/or modify it 7 + * under the terms of the GNU General Public License as published by the 8 + * Free Software Foundation; either version 2 of the License, or (at your 9 + * option) any later version. 10 + * 11 + */ 12 + 13 + #ifndef __LINUX_MCS_H 14 + #define __LINUX_MCS_H 15 + 16 + #define MCS_KEY_MAP(v, c) ((((v) & 0xff) << 16) | ((c) & 0xffff)) 17 + #define MCS_KEY_VAL(v) (((v) >> 16) & 0xff) 18 + #define MCS_KEY_CODE(v) ((v) & 0xffff) 19 + 20 + struct mcs_platform_data { 21 + void (*cfg_pin)(void); 22 + 23 + /* touchscreen */ 24 + unsigned int x_size; 25 + unsigned int y_size; 26 + 27 + /* touchkey */ 28 + const u32 *keymap; 29 + unsigned int keymap_size; 30 + unsigned int key_maxval; 31 + bool no_autorepeat; 32 + }; 33 + 34 + #endif /* __LINUX_MCS_H */
-24
include/linux/i2c/mcs5000_ts.h
··· 1 - /* 2 - * mcs5000_ts.h 3 - * 4 - * Copyright (C) 2009 Samsung Electronics Co.Ltd 5 - * Author: Joonyoung Shim <jy0922.shim@samsung.com> 6 - * 7 - * This program is free software; you can redistribute it and/or modify it 8 - * under the terms of the GNU General Public License as published by the 9 - * Free Software Foundation; either version 2 of the License, or (at your 10 - * option) any later version. 11 - * 12 - */ 13 - 14 - #ifndef __LINUX_MCS5000_TS_H 15 - #define __LINUX_MCS5000_TS_H 16 - 17 - /* platform data for the MELFAS MCS-5000 touchscreen driver */ 18 - struct mcs5000_ts_platform_data { 19 - void (*cfg_pin)(void); 20 - int x_size; 21 - int y_size; 22 - }; 23 - 24 - #endif /* __LINUX_MCS5000_TS_H */