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

Input: add driver FSL MPR121 capacitive touch sensor

This patch adds basic support for Freescale MPR121 capacitive touch
sensor. It's an i2c controller with up to 12 capacitance sensing inputs.

Product information (data sheet, application notes) can be found here:
http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MPR121

Signed-off-by: Zhang Jiejing <jiejing.zhang@freescale.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>

authored by

Zhang Jiejing and committed by
Dmitry Torokhov
36682b81 7cbbb758

+372
+12
drivers/input/keyboard/Kconfig
··· 325 325 To compile this driver as a module, choose M here: the 326 326 module will be called mcs_touchkey. 327 327 328 + config KEYBOARD_MPR121 329 + tristate "Freescale MPR121 Touchkey" 330 + depends on I2C 331 + help 332 + Say Y here if you have Freescale MPR121 touchkey controller 333 + chip in your system. 334 + 335 + If unsure, say N. 336 + 337 + To compile this driver as a module, choose M here: the 338 + module will be called mpr121_touchkey. 339 + 328 340 config KEYBOARD_IMX 329 341 tristate "IMX keypad support" 330 342 depends on ARCH_MXC
+1
drivers/input/keyboard/Makefile
··· 27 27 obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o 28 28 obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o 29 29 obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o 30 + obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o 30 31 obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o 31 32 obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o 32 33 obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
+339
drivers/input/keyboard/mpr121_touchkey.c
··· 1 + /* 2 + * Touchkey driver for Freescale MPR121 Controllor 3 + * 4 + * Copyright (C) 2011 Freescale Semiconductor, Inc. 5 + * Author: Zhang Jiejing <jiejing.zhang@freescale.com> 6 + * 7 + * Based on mcs_touchkey.c 8 + * 9 + * This program is free software; you can redistribute it and/or modify 10 + * it under the terms of the GNU General Public License version 2 as 11 + * published by the Free Software Foundation. 12 + * 13 + */ 14 + 15 + #include <linux/module.h> 16 + #include <linux/init.h> 17 + #include <linux/input.h> 18 + #include <linux/i2c.h> 19 + #include <linux/slab.h> 20 + #include <linux/delay.h> 21 + #include <linux/bitops.h> 22 + #include <linux/interrupt.h> 23 + #include <linux/i2c/mpr121_touchkey.h> 24 + 25 + /* Register definitions */ 26 + #define ELE_TOUCH_STATUS_0_ADDR 0x0 27 + #define ELE_TOUCH_STATUS_1_ADDR 0X1 28 + #define MHD_RISING_ADDR 0x2b 29 + #define NHD_RISING_ADDR 0x2c 30 + #define NCL_RISING_ADDR 0x2d 31 + #define FDL_RISING_ADDR 0x2e 32 + #define MHD_FALLING_ADDR 0x2f 33 + #define NHD_FALLING_ADDR 0x30 34 + #define NCL_FALLING_ADDR 0x31 35 + #define FDL_FALLING_ADDR 0x32 36 + #define ELE0_TOUCH_THRESHOLD_ADDR 0x41 37 + #define ELE0_RELEASE_THRESHOLD_ADDR 0x42 38 + #define AFE_CONF_ADDR 0x5c 39 + #define FILTER_CONF_ADDR 0x5d 40 + 41 + /* 42 + * ELECTRODE_CONF_ADDR: This register configures the number of 43 + * enabled capacitance sensing inputs and its run/suspend mode. 44 + */ 45 + #define ELECTRODE_CONF_ADDR 0x5e 46 + #define AUTO_CONFIG_CTRL_ADDR 0x7b 47 + #define AUTO_CONFIG_USL_ADDR 0x7d 48 + #define AUTO_CONFIG_LSL_ADDR 0x7e 49 + #define AUTO_CONFIG_TL_ADDR 0x7f 50 + 51 + /* Threshold of touch/release trigger */ 52 + #define TOUCH_THRESHOLD 0x0f 53 + #define RELEASE_THRESHOLD 0x0a 54 + /* Masks for touch and release triggers */ 55 + #define TOUCH_STATUS_MASK 0xfff 56 + /* MPR121 has 12 keys */ 57 + #define MPR121_MAX_KEY_COUNT 12 58 + 59 + struct mpr121_touchkey { 60 + struct i2c_client *client; 61 + struct input_dev *input_dev; 62 + unsigned int key_val; 63 + unsigned int statusbits; 64 + unsigned int keycount; 65 + u16 keycodes[MPR121_MAX_KEY_COUNT]; 66 + }; 67 + 68 + struct mpr121_init_register { 69 + int addr; 70 + u8 val; 71 + }; 72 + 73 + static const struct mpr121_init_register init_reg_table[] __devinitconst = { 74 + { MHD_RISING_ADDR, 0x1 }, 75 + { NHD_RISING_ADDR, 0x1 }, 76 + { MHD_FALLING_ADDR, 0x1 }, 77 + { NHD_FALLING_ADDR, 0x1 }, 78 + { NCL_FALLING_ADDR, 0xff }, 79 + { FDL_FALLING_ADDR, 0x02 }, 80 + { FILTER_CONF_ADDR, 0x04 }, 81 + { AFE_CONF_ADDR, 0x0b }, 82 + { AUTO_CONFIG_CTRL_ADDR, 0x0b }, 83 + }; 84 + 85 + static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id) 86 + { 87 + struct mpr121_touchkey *mpr121 = dev_id; 88 + struct i2c_client *client = mpr121->client; 89 + struct input_dev *input = mpr121->input_dev; 90 + unsigned int key_num, key_val, pressed; 91 + int reg; 92 + 93 + reg = i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_1_ADDR); 94 + if (reg < 0) { 95 + dev_err(&client->dev, "i2c read error [%d]\n", reg); 96 + goto out; 97 + } 98 + 99 + reg <<= 8; 100 + reg |= i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_0_ADDR); 101 + if (reg < 0) { 102 + dev_err(&client->dev, "i2c read error [%d]\n", reg); 103 + goto out; 104 + } 105 + 106 + reg &= TOUCH_STATUS_MASK; 107 + /* use old press bit to figure out which bit changed */ 108 + key_num = ffs(reg ^ mpr121->statusbits) - 1; 109 + pressed = reg & (1 << key_num); 110 + mpr121->statusbits = reg; 111 + 112 + key_val = mpr121->keycodes[key_num]; 113 + 114 + input_event(input, EV_MSC, MSC_SCAN, key_num); 115 + input_report_key(input, key_val, pressed); 116 + input_sync(input); 117 + 118 + dev_dbg(&client->dev, "key %d %d %s\n", key_num, key_val, 119 + pressed ? "pressed" : "released"); 120 + 121 + out: 122 + return IRQ_HANDLED; 123 + } 124 + 125 + static int __devinit mpr121_phys_init(const struct mpr121_platform_data *pdata, 126 + struct mpr121_touchkey *mpr121, 127 + struct i2c_client *client) 128 + { 129 + const struct mpr121_init_register *reg; 130 + unsigned char usl, lsl, tl; 131 + int i, t, vdd, ret; 132 + 133 + /* Set up touch/release threshold for ele0-ele11 */ 134 + for (i = 0; i <= MPR121_MAX_KEY_COUNT; i++) { 135 + t = ELE0_TOUCH_THRESHOLD_ADDR + (i * 2); 136 + ret = i2c_smbus_write_byte_data(client, t, TOUCH_THRESHOLD); 137 + if (ret < 0) 138 + goto err_i2c_write; 139 + ret = i2c_smbus_write_byte_data(client, t + 1, 140 + RELEASE_THRESHOLD); 141 + if (ret < 0) 142 + goto err_i2c_write; 143 + } 144 + 145 + /* Set up init register */ 146 + for (i = 0; i < ARRAY_SIZE(init_reg_table); i++) { 147 + reg = &init_reg_table[i]; 148 + ret = i2c_smbus_write_byte_data(client, reg->addr, reg->val); 149 + if (ret < 0) 150 + goto err_i2c_write; 151 + } 152 + 153 + 154 + /* 155 + * Capacitance on sensing input varies and needs to be compensated. 156 + * The internal MPR121-auto-configuration can do this if it's 157 + * registers are set properly (based on pdata->vdd_uv). 158 + */ 159 + vdd = pdata->vdd_uv / 1000; 160 + usl = ((vdd - 700) * 256) / vdd; 161 + lsl = (usl * 65) / 100; 162 + tl = (usl * 90) / 100; 163 + ret = i2c_smbus_write_byte_data(client, AUTO_CONFIG_USL_ADDR, usl); 164 + ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_LSL_ADDR, lsl); 165 + ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_TL_ADDR, tl); 166 + ret |= i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, 167 + mpr121->keycount); 168 + if (ret != 0) 169 + goto err_i2c_write; 170 + 171 + dev_dbg(&client->dev, "set up with %x keys.\n", mpr121->keycount); 172 + 173 + return 0; 174 + 175 + err_i2c_write: 176 + dev_err(&client->dev, "i2c write error: %d\n", ret); 177 + return ret; 178 + } 179 + 180 + static int __devinit mpr_touchkey_probe(struct i2c_client *client, 181 + const struct i2c_device_id *id) 182 + { 183 + const struct mpr121_platform_data *pdata = client->dev.platform_data; 184 + struct mpr121_touchkey *mpr121; 185 + struct input_dev *input_dev; 186 + int error; 187 + int i; 188 + 189 + if (!pdata) { 190 + dev_err(&client->dev, "no platform data defined\n"); 191 + return -EINVAL; 192 + } 193 + 194 + if (!pdata->keymap || !pdata->keymap_size) { 195 + dev_err(&client->dev, "missing keymap data\n"); 196 + return -EINVAL; 197 + } 198 + 199 + if (pdata->keymap_size > MPR121_MAX_KEY_COUNT) { 200 + dev_err(&client->dev, "too many keys defined\n"); 201 + return -EINVAL; 202 + } 203 + 204 + if (!client->irq) { 205 + dev_err(&client->dev, "irq number should not be zero\n"); 206 + return -EINVAL; 207 + } 208 + 209 + mpr121 = kzalloc(sizeof(struct mpr121_touchkey), GFP_KERNEL); 210 + input_dev = input_allocate_device(); 211 + if (!mpr121 || !input_dev) { 212 + dev_err(&client->dev, "Failed to allocate memory\n"); 213 + error = -ENOMEM; 214 + goto err_free_mem; 215 + } 216 + 217 + mpr121->client = client; 218 + mpr121->input_dev = input_dev; 219 + mpr121->keycount = pdata->keymap_size; 220 + 221 + input_dev->name = "Freescale MPR121 Touchkey"; 222 + input_dev->id.bustype = BUS_I2C; 223 + input_dev->dev.parent = &client->dev; 224 + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); 225 + 226 + input_dev->keycode = mpr121->keycodes; 227 + input_dev->keycodesize = sizeof(mpr121->keycodes[0]); 228 + input_dev->keycodemax = mpr121->keycount; 229 + 230 + for (i = 0; i < pdata->keymap_size; i++) { 231 + input_set_capability(input_dev, EV_KEY, pdata->keymap[i]); 232 + mpr121->keycodes[i] = pdata->keymap[i]; 233 + } 234 + 235 + error = mpr121_phys_init(pdata, mpr121, client); 236 + if (error) { 237 + dev_err(&client->dev, "Failed to init register\n"); 238 + goto err_free_mem; 239 + } 240 + 241 + error = request_threaded_irq(client->irq, NULL, 242 + mpr_touchkey_interrupt, 243 + IRQF_TRIGGER_FALLING, 244 + client->dev.driver->name, mpr121); 245 + if (error) { 246 + dev_err(&client->dev, "Failed to register interrupt\n"); 247 + goto err_free_mem; 248 + } 249 + 250 + error = input_register_device(input_dev); 251 + if (error) 252 + goto err_free_irq; 253 + 254 + i2c_set_clientdata(client, mpr121); 255 + device_init_wakeup(&client->dev, pdata->wakeup); 256 + 257 + return 0; 258 + 259 + err_free_irq: 260 + free_irq(client->irq, mpr121); 261 + err_free_mem: 262 + input_free_device(input_dev); 263 + kfree(mpr121); 264 + return error; 265 + } 266 + 267 + static int __devexit mpr_touchkey_remove(struct i2c_client *client) 268 + { 269 + struct mpr121_touchkey *mpr121 = i2c_get_clientdata(client); 270 + 271 + free_irq(client->irq, mpr121); 272 + input_unregister_device(mpr121->input_dev); 273 + kfree(mpr121); 274 + 275 + return 0; 276 + } 277 + 278 + #ifdef CONFIG_PM_SLEEP 279 + static int mpr_suspend(struct device *dev) 280 + { 281 + struct i2c_client *client = to_i2c_client(dev); 282 + 283 + if (device_may_wakeup(&client->dev)) 284 + enable_irq_wake(client->irq); 285 + 286 + i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, 0x00); 287 + 288 + return 0; 289 + } 290 + 291 + static int mpr_resume(struct device *dev) 292 + { 293 + struct i2c_client *client = to_i2c_client(dev); 294 + struct mpr121_touchkey *mpr121 = i2c_get_clientdata(client); 295 + 296 + if (device_may_wakeup(&client->dev)) 297 + disable_irq_wake(client->irq); 298 + 299 + i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, 300 + mpr121->keycount); 301 + 302 + return 0; 303 + } 304 + #endif 305 + 306 + static SIMPLE_DEV_PM_OPS(mpr121_touchkey_pm_ops, mpr_suspend, mpr_resume); 307 + 308 + static const struct i2c_device_id mpr121_id[] = { 309 + { "mpr121_touchkey", 0 }, 310 + { } 311 + }; 312 + MODULE_DEVICE_TABLE(i2c, mpr121_id); 313 + 314 + static struct i2c_driver mpr_touchkey_driver = { 315 + .driver = { 316 + .name = "mpr121", 317 + .owner = THIS_MODULE, 318 + .pm = &mpr121_touchkey_pm_ops, 319 + }, 320 + .id_table = mpr121_id, 321 + .probe = mpr_touchkey_probe, 322 + .remove = __devexit_p(mpr_touchkey_remove), 323 + }; 324 + 325 + static int __init mpr_touchkey_init(void) 326 + { 327 + return i2c_add_driver(&mpr_touchkey_driver); 328 + } 329 + module_init(mpr_touchkey_init); 330 + 331 + static void __exit mpr_touchkey_exit(void) 332 + { 333 + i2c_del_driver(&mpr_touchkey_driver); 334 + } 335 + module_exit(mpr_touchkey_exit); 336 + 337 + MODULE_LICENSE("GPL"); 338 + MODULE_AUTHOR("Zhang Jiejing <jiejing.zhang@freescale.com>"); 339 + MODULE_DESCRIPTION("Touch Key driver for Freescale MPR121 Chip");
+20
include/linux/i2c/mpr121_touchkey.h
··· 1 + /* Header file for Freescale MPR121 Capacitive Touch Sensor */ 2 + 3 + #ifndef _MPR121_TOUCHKEY_H 4 + #define _MPR121_TOUCHKEY_H 5 + 6 + /** 7 + * struct mpr121_platform_data - platform data for mpr121 sensor 8 + * @keymap: pointer to array of KEY_* values representing keymap 9 + * @keymap_size: size of the keymap 10 + * @wakeup: configure the button as a wake-up source 11 + * @vdd_uv: VDD voltage in uV 12 + */ 13 + struct mpr121_platform_data { 14 + const unsigned short *keymap; 15 + unsigned int keymap_size; 16 + bool wakeup; 17 + int vdd_uv; 18 + }; 19 + 20 + #endif /* _MPR121_TOUCHKEY_H */