linux/drivers/input/keyboard/mcs_touchkey.c
<<
>>
Prefs
   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
  35enum mcs_touchkey_type {
  36        MCS5000_TOUCHKEY,
  37        MCS5080_TOUCHKEY,
  38};
  39
  40struct mcs_touchkey_chip {
  41        unsigned int status_reg;
  42        unsigned int pressbit;
  43        unsigned int press_invert;
  44        unsigned int baseval;
  45};
  46
  47struct 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
  56static 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
  97static 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
 186err_free_irq:
 187        free_irq(client->irq, data);
 188err_free_mem:
 189        input_free_device(input_dev);
 190        kfree(data);
 191        return error;
 192}
 193
 194static 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
 205static const struct i2c_device_id mcs_touchkey_id[] = {
 206        { "mcs5000_touchkey", MCS5000_TOUCHKEY },
 207        { "mcs5080_touchkey", MCS5080_TOUCHKEY },
 208        { }
 209};
 210MODULE_DEVICE_TABLE(i2c, mcs_touchkey_id);
 211
 212static 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
 222static int __init mcs_touchkey_init(void)
 223{
 224        return i2c_add_driver(&mcs_touchkey_driver);
 225}
 226
 227static void __exit mcs_touchkey_exit(void)
 228{
 229        i2c_del_driver(&mcs_touchkey_driver);
 230}
 231
 232module_init(mcs_touchkey_init);
 233module_exit(mcs_touchkey_exit);
 234
 235/* Module information */
 236MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
 237MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>");
 238MODULE_DESCRIPTION("Touchkey driver for MELFAS MCS5000/5080 controller");
 239MODULE_LICENSE("GPL");
 240