linux/drivers/input/keyboard/mcs_touchkey.c
<<
>>
Prefs
   1/*
   2 * 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/i2c.h>
  16#include <linux/interrupt.h>
  17#include <linux/input.h>
  18#include <linux/irq.h>
  19#include <linux/slab.h>
  20#include <linux/platform_data/mcs.h>
  21#include <linux/pm.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        void (*poweron)(bool);
  49
  50        struct i2c_client *client;
  51        struct input_dev *input_dev;
  52        struct mcs_touchkey_chip chip;
  53        unsigned int key_code;
  54        unsigned int key_val;
  55        unsigned short keycodes[];
  56};
  57
  58static irqreturn_t mcs_touchkey_interrupt(int irq, void *dev_id)
  59{
  60        struct mcs_touchkey_data *data = dev_id;
  61        struct mcs_touchkey_chip *chip = &data->chip;
  62        struct i2c_client *client = data->client;
  63        struct input_dev *input = data->input_dev;
  64        unsigned int key_val;
  65        unsigned int pressed;
  66        int val;
  67
  68        val = i2c_smbus_read_byte_data(client, chip->status_reg);
  69        if (val < 0) {
  70                dev_err(&client->dev, "i2c read error [%d]\n", val);
  71                goto out;
  72        }
  73
  74        pressed = (val & (1 << chip->pressbit)) >> chip->pressbit;
  75        if (chip->press_invert)
  76                pressed ^= chip->press_invert;
  77
  78        /* key_val is 0 when released, so we should use key_val of press. */
  79        if (pressed) {
  80                key_val = val & (0xff >> (8 - chip->pressbit));
  81                if (!key_val)
  82                        goto out;
  83                key_val -= chip->baseval;
  84                data->key_code = data->keycodes[key_val];
  85                data->key_val = key_val;
  86        }
  87
  88        input_event(input, EV_MSC, MSC_SCAN, data->key_val);
  89        input_report_key(input, data->key_code, pressed);
  90        input_sync(input);
  91
  92        dev_dbg(&client->dev, "key %d %d %s\n", data->key_val, data->key_code,
  93                pressed ? "pressed" : "released");
  94
  95 out:
  96        return IRQ_HANDLED;
  97}
  98
  99static int mcs_touchkey_probe(struct i2c_client *client,
 100                const struct i2c_device_id *id)
 101{
 102        const struct mcs_platform_data *pdata;
 103        struct mcs_touchkey_data *data;
 104        struct input_dev *input_dev;
 105        unsigned int fw_reg;
 106        int fw_ver;
 107        int error;
 108        int i;
 109
 110        pdata = dev_get_platdata(&client->dev);
 111        if (!pdata) {
 112                dev_err(&client->dev, "no platform data defined\n");
 113                return -EINVAL;
 114        }
 115
 116        data = kzalloc(sizeof(struct mcs_touchkey_data) +
 117                        sizeof(data->keycodes[0]) * (pdata->key_maxval + 1),
 118                        GFP_KERNEL);
 119        input_dev = input_allocate_device();
 120        if (!data || !input_dev) {
 121                dev_err(&client->dev, "Failed to allocate memory\n");
 122                error = -ENOMEM;
 123                goto err_free_mem;
 124        }
 125
 126        data->client = client;
 127        data->input_dev = input_dev;
 128
 129        if (id->driver_data == MCS5000_TOUCHKEY) {
 130                data->chip.status_reg = MCS5000_TOUCHKEY_STATUS;
 131                data->chip.pressbit = MCS5000_TOUCHKEY_STATUS_PRESS;
 132                data->chip.baseval = MCS5000_TOUCHKEY_BASE_VAL;
 133                fw_reg = MCS5000_TOUCHKEY_FW;
 134        } else {
 135                data->chip.status_reg = MCS5080_TOUCHKEY_STATUS;
 136                data->chip.pressbit = MCS5080_TOUCHKEY_STATUS_PRESS;
 137                data->chip.press_invert = 1;
 138                data->chip.baseval = MCS5080_TOUCHKEY_BASE_VAL;
 139                fw_reg = MCS5080_TOUCHKEY_FW;
 140        }
 141
 142        fw_ver = i2c_smbus_read_byte_data(client, fw_reg);
 143        if (fw_ver < 0) {
 144                error = fw_ver;
 145                dev_err(&client->dev, "i2c read error[%d]\n", error);
 146                goto err_free_mem;
 147        }
 148        dev_info(&client->dev, "Firmware version: %d\n", fw_ver);
 149
 150        input_dev->name = "MELFAS MCS Touchkey";
 151        input_dev->id.bustype = BUS_I2C;
 152        input_dev->dev.parent = &client->dev;
 153        input_dev->evbit[0] = BIT_MASK(EV_KEY);
 154        if (!pdata->no_autorepeat)
 155                input_dev->evbit[0] |= BIT_MASK(EV_REP);
 156        input_dev->keycode = data->keycodes;
 157        input_dev->keycodesize = sizeof(data->keycodes[0]);
 158        input_dev->keycodemax = pdata->key_maxval + 1;
 159
 160        for (i = 0; i < pdata->keymap_size; i++) {
 161                unsigned int val = MCS_KEY_VAL(pdata->keymap[i]);
 162                unsigned int code = MCS_KEY_CODE(pdata->keymap[i]);
 163
 164                data->keycodes[val] = code;
 165                __set_bit(code, input_dev->keybit);
 166        }
 167
 168        input_set_capability(input_dev, EV_MSC, MSC_SCAN);
 169        input_set_drvdata(input_dev, data);
 170
 171        if (pdata->cfg_pin)
 172                pdata->cfg_pin();
 173
 174        if (pdata->poweron) {
 175                data->poweron = pdata->poweron;
 176                data->poweron(true);
 177        }
 178
 179        error = request_threaded_irq(client->irq, NULL, mcs_touchkey_interrupt,
 180                                     IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
 181                                     client->dev.driver->name, data);
 182        if (error) {
 183                dev_err(&client->dev, "Failed to register interrupt\n");
 184                goto err_free_mem;
 185        }
 186
 187        error = input_register_device(input_dev);
 188        if (error)
 189                goto err_free_irq;
 190
 191        i2c_set_clientdata(client, data);
 192        return 0;
 193
 194err_free_irq:
 195        free_irq(client->irq, data);
 196err_free_mem:
 197        input_free_device(input_dev);
 198        kfree(data);
 199        return error;
 200}
 201
 202static int mcs_touchkey_remove(struct i2c_client *client)
 203{
 204        struct mcs_touchkey_data *data = i2c_get_clientdata(client);
 205
 206        free_irq(client->irq, data);
 207        if (data->poweron)
 208                data->poweron(false);
 209        input_unregister_device(data->input_dev);
 210        kfree(data);
 211
 212        return 0;
 213}
 214
 215static void mcs_touchkey_shutdown(struct i2c_client *client)
 216{
 217        struct mcs_touchkey_data *data = i2c_get_clientdata(client);
 218
 219        if (data->poweron)
 220                data->poweron(false);
 221}
 222
 223#ifdef CONFIG_PM_SLEEP
 224static int mcs_touchkey_suspend(struct device *dev)
 225{
 226        struct mcs_touchkey_data *data = dev_get_drvdata(dev);
 227        struct i2c_client *client = data->client;
 228
 229        /* Disable the work */
 230        disable_irq(client->irq);
 231
 232        /* Finally turn off the power */
 233        if (data->poweron)
 234                data->poweron(false);
 235
 236        return 0;
 237}
 238
 239static int mcs_touchkey_resume(struct device *dev)
 240{
 241        struct mcs_touchkey_data *data = dev_get_drvdata(dev);
 242        struct i2c_client *client = data->client;
 243
 244        /* Enable the device first */
 245        if (data->poweron)
 246                data->poweron(true);
 247
 248        /* Enable irq again */
 249        enable_irq(client->irq);
 250
 251        return 0;
 252}
 253#endif
 254
 255static SIMPLE_DEV_PM_OPS(mcs_touchkey_pm_ops,
 256                         mcs_touchkey_suspend, mcs_touchkey_resume);
 257
 258static const struct i2c_device_id mcs_touchkey_id[] = {
 259        { "mcs5000_touchkey", MCS5000_TOUCHKEY },
 260        { "mcs5080_touchkey", MCS5080_TOUCHKEY },
 261        { }
 262};
 263MODULE_DEVICE_TABLE(i2c, mcs_touchkey_id);
 264
 265static struct i2c_driver mcs_touchkey_driver = {
 266        .driver = {
 267                .name   = "mcs_touchkey",
 268                .pm     = &mcs_touchkey_pm_ops,
 269        },
 270        .probe          = mcs_touchkey_probe,
 271        .remove         = mcs_touchkey_remove,
 272        .shutdown       = mcs_touchkey_shutdown,
 273        .id_table       = mcs_touchkey_id,
 274};
 275
 276module_i2c_driver(mcs_touchkey_driver);
 277
 278/* Module information */
 279MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
 280MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>");
 281MODULE_DESCRIPTION("Touchkey driver for MELFAS MCS5000/5080 controller");
 282MODULE_LICENSE("GPL");
 283