Re: [PATCH] input: add mpr121 capacitive touchkey driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Christoph,

I'm sorry for my poor English grammar.
I'll try to fix these typo, If still have error, please point me out.

2011/4/12 Christoph Fritz <chf.fritz@xxxxxxxxxxxxxx>
>
> Hi Zhang,
>
>  this is just a superficial review of your driver.
>
> thanks,
>  Christoph
>
> On Tue, Apr 12, 2011 at 01:02:17AM +0800, Jiejing Zhang wrote:
> > From: Zhang Jiejing <jiejing.zhang@xxxxxxxxxxxxx>
> >
> > This touchkey driver is based on Freescale mpr121 capacitive
> > touch sensor controller.
> >
> > It can support up to 12 keys, it use i2c interface.
>
> typo

-> It can supports up to 12 electrodes, and using i2c interface.
>
> >
> > The product infomation(data sheet, application notes) can
> > be found under this link:
> > http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MPR121
> >
> > Signed-off-by: Zhang Jiejing <jiejing.zhang@xxxxxxxxxxxxx>
> > ---
> >  drivers/input/keyboard/Kconfig  |   12 ++
> >  drivers/input/keyboard/Makefile |    1 +
> >  drivers/input/keyboard/mpr121.c |  301 +++++++++++++++++++++++++++++++++++++++
> >  include/linux/i2c/mpr.h         |   64 ++++++++
> >  4 files changed, 378 insertions(+), 0 deletions(-)
> >  create mode 100644 drivers/input/keyboard/mpr121.c
> >  create mode 100644 include/linux/i2c/mpr.h
> >
> > diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
> > index b16bed0..0666e1e 100644
> > --- a/drivers/input/keyboard/Kconfig
> > +++ b/drivers/input/keyboard/Kconfig
> > @@ -520,6 +520,18 @@ config KEYBOARD_XTKBD
> >         To compile this driver as a module, choose M here: the
> >         module will be called xtkbd.
> >
> > +config KEYBOARD_MPR121
> > +     tristate "Freescale MPR121 Touchkey Driver"
> > +     depends on I2C
> > +     help
> > +       Say Y here if you have the  touchkey Freescale mpr121 capacitive
>
> typo: two spaces

will fix.
>
> > +       touch sensor controller in your system.
> > +
> > +       If unsure, say N.
> > +
> > +       To compile this driver as a module, choose M here
> > +endif
> > +
> >  config KEYBOARD_W90P910
> >       tristate "W90P910 Matrix Keypad support"
> >       depends on ARCH_W90X900
>
> - add: "To compile this driver as a module, choose M here: the
>  module will be called mpr121."
> - "endif" is wrong here, it breaks 'make config'
> - maybe a more alphabetic approach in this list of drivers would
>  be nice

yes. I will.
>
> > diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
> > index 878e6c2..b110ca8 100644
> > --- a/drivers/input/keyboard/Makefile
> > +++ b/drivers/input/keyboard/Makefile
> > @@ -47,4 +47,5 @@ obj-$(CONFIG_KEYBOARD_TEGRA)                += tegra-kbc.o
> >  obj-$(CONFIG_KEYBOARD_TNETV107X)     += tnetv107x-keypad.o
> >  obj-$(CONFIG_KEYBOARD_TWL4030)               += twl4030_keypad.o
> >  obj-$(CONFIG_KEYBOARD_XTKBD)         += xtkbd.o
> > +obj-$(CONFIG_KEYBOARD_MPR121)                += mpr121.o
> >  obj-$(CONFIG_KEYBOARD_W90P910)               += w90p910_keypad.o
>
> for better examination, please mind alphabetic order

will do.
>
> > diff --git a/drivers/input/keyboard/mpr121.c b/drivers/input/keyboard/mpr121.c
> > new file mode 100644
> > index 0000000..ee149a8
> > --- /dev/null
> > +++ b/drivers/input/keyboard/mpr121.c
> > @@ -0,0 +1,301 @@
> > +/*
> > + * Touchkey driver for Freescale MPR121 Controllor
> > + *
> > + * Copyright (C) 2011 Freescale Semiconductor, Inc.
> > + * Author: Zhang Jiejing <jiejing.zhang@xxxxxxxxxxxxx>
> > + *
> > + * Based on mcs_touchkey.c
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/init.h>
> > +#include <linux/i2c.h>
> > +#include <linux/i2c/mpr.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/input.h>
> > +#include <linux/irq.h>
>
> do not use irq.h directly

ok.
>
> > +#include <linux/slab.h>
> > +#include <linux/delay.h>
> > +#include <linux/bitops.h>
> > +
> > +struct mpr121_touchkey_data {
> > +     struct i2c_client       *client;
> > +     struct input_dev        *input_dev;
> > +     unsigned int            key_val;
> > +     int                     statusbits;
> > +     int                     keycount;
> > +     u16                     keycodes[MPR121_MAX_KEY_COUNT];
> > +};
> > +
> > +struct mpr121_init_register {
> > +     int addr;
> > +     u8 val;
> > +};
> > +
> > +static struct mpr121_init_register init_reg_table[] = {
> > +     {MHD_RISING_ADDR,       0x1},
> > +     {NHD_RISING_ADDR,       0x1},
> > +     {MHD_FALLING_ADDR,      0x1},
> > +     {NHD_FALLING_ADDR,      0x1},
> > +     {NCL_FALLING_ADDR,      0xff},
> > +     {FDL_FALLING_ADDR,      0x02},
> > +     {FILTER_CONF_ADDR,      0x04},
> > +     {AFE_CONF_ADDR,         0x0b},
> > +     {AUTO_CONFIG_CTRL_ADDR, 0x0b},
> > +};
> > +
> > +static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id)
> > +{
> > +     struct mpr121_touchkey_data *data = dev_id;
> > +     struct i2c_client *client = data->client;
> > +     struct input_dev *input = data->input_dev;
> > +     unsigned int key_num, pressed;
> > +     int reg;
> > +
> > +     reg = i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_1_ADDR);
> > +     if (reg < 0) {
> > +             dev_err(&client->dev, "i2c read error [%d]\n", reg);
> > +             goto out;
> > +     }
> > +
> > +     reg <<= 8;
> > +     reg |= i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_0_ADDR);
> > +     if (reg < 0) {
> > +             dev_err(&client->dev, "i2c read error [%d]\n", reg);
> > +             goto out;
> > +     }
> > +
> > +     reg &= TOUCH_STATUS_MASK;
> > +     /* use old press bit to figure out which bit changed */
> > +     key_num = ffs(reg ^ data->statusbits) - 1;
> > +     /* use the bit check the press status */
>
> typo

this comment will remove.
>
> > +     pressed = (reg & (1 << (key_num))) >> key_num;
> > +     data->statusbits = reg;
> > +     data->key_val = data->keycodes[key_num];
> > +
> > +     input_event(input, EV_MSC, MSC_SCAN, data->key_val);
> > +     input_report_key(input, data->key_val, pressed);
> > +     input_sync(input);
> > +
> > +     dev_dbg(&client->dev, "key %d %d %s\n", key_num, data->key_val,
> > +             pressed ? "pressed" : "released");
> > +
> > +out:
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +static int mpr121_phys_init(struct mpr121_platform_data *pdata,
> > +                         struct mpr121_touchkey_data *data,
> > +                         struct i2c_client *client)
> > +{
> > +     struct mpr121_init_register *reg;
> > +     unsigned char usl, lsl, tl;
> > +     int i, t, vdd, ret;
> > +
> > +     /* setup touch/release threshold for ele0-ele11 */
> > +     for (i = 0; i <= MPR121_MAX_KEY_COUNT; i++) {
> > +             t = ELE0_TOUCH_THRESHOLD_ADDR + (i * 2);
> > +             ret = i2c_smbus_write_byte_data(client, t, TOUCH_THRESHOLD);
> > +             if (ret < 0)
> > +                     goto err_i2c_write;
> > +             ret = i2c_smbus_write_byte_data(client, t + 1,
> > +                                             RELEASE_THRESHOLD);
> > +             if (ret < 0)
> > +                     goto err_i2c_write;
> > +     }
> > +     /* setup init register */
> > +     for (i = 0; i < ARRAY_SIZE(init_reg_table); i++) {
> > +             reg = &init_reg_table[i];
> > +             ret = i2c_smbus_write_byte_data(client, reg->addr, reg->val);
> > +             if (ret < 0)
> > +                     goto err_i2c_write;
> > +     }
> > +     /* setup auto-register by vdd,the formula please ref: AN3889
> > +      * These register *must* set acrodding your VDD voltage supply
> > +      * to the chip*/
>
> typo

is this right?
    /* setup Auto Config Registers by VDD.
     * The formula was written in AN3889.
     * These registers *must* be set by VDD voltage.
     */
>
> > +     vdd = pdata->vdd_uv / 1000;
> > +     usl = ((vdd - 700) * 256) / vdd;
> > +     lsl = (usl * 65) / 100;
> > +     tl = (usl * 90) / 100;
> > +     ret = i2c_smbus_write_byte_data(client, AUTO_CONFIG_USL_ADDR, usl);
> > +     ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_LSL_ADDR, lsl);
> > +     ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_TL_ADDR, tl);
> > +     ret |= i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR,
> > +                                     data->keycount);
> > +     if (ret != 0)
> > +             goto err_i2c_write;
> > +
> > +     dev_info(&client->dev, "mpr121: config as enable %x of electrode.\n",
>
> typo ?

-> dev_dbg(&client->dev, "mpr121: setup with %x electrodes.\n",

>
> > +              data->keycount);
> > +
> > +     return 0;
> > +
> > +err_i2c_write:
> > +     dev_err(&client->dev, "i2c write error: %d\n", ret);
> > +     return ret;
> > +}
> > +
> > +static int __devinit mpr_touchkey_probe(struct i2c_client *client,
> > +                                     const struct i2c_device_id *id)
> > +{
> > +     struct mpr121_platform_data *pdata;
> > +     struct mpr121_touchkey_data *data;
> > +     struct input_dev *input_dev;
> > +     int error;
> > +     int i;
> > +
> > +     pdata = client->dev.platform_data;
> > +     if (!pdata) {
> > +             dev_err(&client->dev, "no platform data defined\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     if (!client->irq) {
> > +             dev_err(&client->dev, "The irq number should not be zero\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     data = kzalloc(sizeof(struct mpr121_touchkey_data), GFP_KERNEL);
> > +     input_dev = input_allocate_device();
> > +     if (!data || !input_dev) {
> > +             dev_err(&client->dev, "Falied to allocate memory\n");
> > +             error = -ENOMEM;
> > +             goto err_free_mem;
> > +     }
> > +
> > +     data->client = client;
> > +     data->input_dev = input_dev;
> > +     data->keycount = pdata->keycount;
> > +
> > +     if (data->keycount > MPR121_MAX_KEY_COUNT) {
> > +             dev_err(&client->dev, "Too many key defined\n");
>
> typo

-> dev_err(&client->dev, "too much keys defined\n");
>
> > +             error = -EINVAL;
> > +             goto err_free_mem;
> > +     }
> > +
> > +     error = mpr121_phys_init(pdata, data, client);
> > +     if (error) {
> > +             dev_err(&client->dev, "Failed to init register\n");
> > +             goto err_free_mem;
> > +     }
> > +
> > +     i2c_set_clientdata(client, data);
> > +
> > +     input_dev->name = "FSL MPR121 Touchkey";
> > +     input_dev->id.bustype = BUS_I2C;
> > +     input_dev->dev.parent = &client->dev;
> > +     input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
> > +     input_dev->keycode = pdata->matrix;
> > +     input_dev->keycodesize = sizeof(pdata->matrix[0]);
> > +     input_dev->keycodemax = data->keycount;
> > +
> > +     for (i = 0; i < input_dev->keycodemax; i++) {
> > +             __set_bit(pdata->matrix[i], input_dev->keybit);
> > +             data->keycodes[i] = pdata->matrix[i];
> > +     }
> > +
> > +     input_set_capability(input_dev, EV_MSC, MSC_SCAN);
> > +     input_set_drvdata(input_dev, data);
> > +
> > +     error = request_threaded_irq(client->irq, NULL,
> > +                                  mpr_touchkey_interrupt,
> > +                                  IRQF_TRIGGER_FALLING,
> > +                                  client->dev.driver->name, data);
> > +     if (error) {
> > +             dev_err(&client->dev, "Failed to register interrupt\n");
> > +             goto err_free_mem;
> > +     }
> > +
> > +     error = input_register_device(input_dev);
> > +     if (error)
> > +             goto err_free_irq;
> > +     i2c_set_clientdata(client, data);
> > +     device_init_wakeup(&client->dev, pdata->wakeup);
> > +     dev_info(&client->dev, "Mpr121 touch keyboard init success.\n");
> > +     return 0;
> > +
> > +err_free_irq:
> > +     free_irq(client->irq, data);
> > +err_free_mem:
> > +     input_free_device(input_dev);
> > +     kfree(data);
> > +     return error;
> > +}
> > +
> > +static int __devexit mpr_touchkey_remove(struct i2c_client *client)
> > +{
> > +     struct mpr121_touchkey_data *data = i2c_get_clientdata(client);
> > +
> > +     free_irq(client->irq, data);
> > +     input_unregister_device(data->input_dev);
> > +     kfree(data);
> > +
> > +     return 0;
> > +}
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int mpr_suspend(struct device *dev)
> > +{
> > +     struct i2c_client *client = to_i2c_client(dev);
> > +
> > +     if (device_may_wakeup(&client->dev))
> > +             enable_irq_wake(client->irq);
> > +
> > +     i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, 0x00);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mpr_resume(struct device *dev)
> > +{
> > +     struct i2c_client *client = to_i2c_client(dev);
> > +     struct mpr121_touchkey_data *data = i2c_get_clientdata(client);
> > +
> > +     if (device_may_wakeup(&client->dev))
> > +             disable_irq_wake(client->irq);
> > +
> > +     i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, data->keycount);
> > +
> > +     return 0;
> > +}
> > +#endif
> > +
> > +static const struct i2c_device_id mpr121_id[] = {
> > +     {"mpr121_touchkey", 0},
> > +     { }
> > +};
> > +
> > +static SIMPLE_DEV_PM_OPS(mpr121_touchkey_pm_ops, mpr_suspend, mpr_resume);
> > +
> > +static struct i2c_driver mpr_touchkey_driver = {
> > +     .driver = {
> > +             .name   = "mpr121",
> > +             .owner  = THIS_MODULE,
> > +             .pm     = &mpr121_touchkey_pm_ops,
>
> I would add a ifdef CONFIG_PM_SLEEP
>
> > +     },
> > +     .id_table       = mpr121_id,
> > +     .probe          = mpr_touchkey_probe,
> > +     .remove         = __devexit_p(mpr_touchkey_remove),
> > +};
> > +
> > +static int __init mpr_touchkey_init(void)
> > +{
> > +     return i2c_add_driver(&mpr_touchkey_driver);
> > +}
> > +
> > +static void __exit mpr_touchkey_exit(void)
> > +{
> > +     i2c_del_driver(&mpr_touchkey_driver);
> > +}
> > +
> > +module_init(mpr_touchkey_init);
> > +module_exit(mpr_touchkey_exit);
> > +
> > +MODULE_LICENSE("GPL");
> > +MODULE_AUTHOR("Zhang Jiejing <jiejing.zhang@xxxxxxxxxxxxx>");
> > +MODULE_DESCRIPTION("Touch Key driver for FSL MPR121 Chip");
> > diff --git a/include/linux/i2c/mpr.h b/include/linux/i2c/mpr.h
> > new file mode 100644
> > index 0000000..52d6e33
> > --- /dev/null
> > +++ b/include/linux/i2c/mpr.h
> > @@ -0,0 +1,64 @@
> > +/* mpr.h - Header file for Freescale mpr121 capacitive touch sensor
>
> no filename please

will do.
>
> > + * controllor */
> > +
> > +#ifndef MPR_H
> > +#define MPR_H
> > +
> > +/* Register definitions */
> > +#define ELE_TOUCH_STATUS_0_ADDR      0x0
> > +#define ELE_TOUCH_STATUS_1_ADDR      0X1
> > +#define MHD_RISING_ADDR              0x2b
> > +#define NHD_RISING_ADDR              0x2c
> > +#define NCL_RISING_ADDR              0x2d
> > +#define FDL_RISING_ADDR              0x2e
> > +#define MHD_FALLING_ADDR     0x2f
> > +#define NHD_FALLING_ADDR     0x30
> > +#define NCL_FALLING_ADDR     0x31
> > +#define FDL_FALLING_ADDR     0x32
> > +#define ELE0_TOUCH_THRESHOLD_ADDR    0x41
> > +#define ELE0_RELEASE_THRESHOLD_ADDR  0x42
> > +/* ELE0...ELE11's threshold will set in a loop */
>
> typo

-> /* ELE0...ELE11's threshold will be set in a loop */
>
> > +#define AFE_CONF_ADDR                        0x5c
> > +#define FILTER_CONF_ADDR             0x5d
> > +
> > +/* ELECTRODE_CONF: this register is most important register, it
> > + * control how many of electrode is enabled. If you set this register
> > + * to 0x0, it make the sensor going to suspend mode. Other value(low
> > + * bit is non-zero) will make the sensor into Run mode.  This register
> > + * should be write at last.
>
> typo

->  * should be written in the end.

>
> > + */
> > +#define ELECTRODE_CONF_ADDR          0x5e
> > +#define AUTO_CONFIG_CTRL_ADDR                0x7b
> > +/* AUTO_CONFIG_USL: Upper Limit for auto baseline search, this
> > + * register is related to VDD supplied on your board, the value of
> > + * this register is calc by EQ: `((VDD-0.7)/VDD) * 256`.
>
> typo

->  * this register is calculated by EQ: `((VDD-0.7)/VDD) * 256`.

>
> > + * AUTO_CONFIG_LSL: Low Limit of auto baseline search. This is 65% of
> > + * USL AUTO_CONFIG_TL: The Traget Level of auto baseline search, This
> > + * is 90% of USL  */
>
> typo

->  * USL AUTO_CONFIG_TL: The traget level of auto baseline search, it is
->  * 90% of USL */

>
> > +#define AUTO_CONFIG_USL_ADDR         0x7d
> > +#define AUTO_CONFIG_LSL_ADDR         0x7e
> > +#define AUTO_CONFIG_TL_ADDR          0x7f
> > +
> > +/* Threshold of touch/release trigger */
> > +#define TOUCH_THRESHOLD                      0x0f
> > +#define RELEASE_THRESHOLD            0x0a
> > +/* Mask Button bits of STATUS_0 & STATUS_1 register */
>
> and or bit-and?

don't need and or bit-and, it just means low 12 bits of status
register is touch key's status.
-> /* low 12 bits of status register is touch key's status */
>
> > +#define TOUCH_STATUS_MASK            0xfff
> > +/* MPR121 have 12 electrodes */
> > +#define MPR121_MAX_KEY_COUNT         12
> > +
> > +
> > +/**
> > + * @keycount: how many key maped
>
> typo: "keys" ?

->  * @keycount: how many keys mapped
>
> > + * @vdd_uv: voltage of vdd supply the chip in uV
>
> - typo
> - uppercase for vdd

->  * @vdd_uv: VDD voltage in uV

>
> > + * @matrix: maxtrix of keys
> > + * @wakeup: can key wake up system.
>
> typo

->  * @wakeup: can wake up system or not
>
> > + */
> > +struct mpr121_platform_data {
> > +     u16 keycount;
> > +     u16 *matrix;
> > +     int wakeup;
> > +     int vdd_uv;
> > +};
> > +
> > +#endif
> > --
> > 1.7.1
> >
> > --
> > 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.htmln Tue, Apr 12, 2011 at 01:02:17AM +0800, Jiejing Zhang wrote:

:-(
sorry again for my horrible grammar.

Jiejing
--
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


[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux