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

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

 



Hi Dmitry,

2011/4/12 Dmitry Torokhov <dmitry.torokhov@xxxxxxxxx>:
> Hi Jiejing,
>
> 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.
>>
>> 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
>>
>
> MOstly reasonable. In addition to Christoph's comments:
>
>> +
>> +static struct mpr121_init_register init_reg_table[] = {
>
> const? And also __devinitconst?
will do.
>
>> +     {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 */
>> +     pressed = (reg & (1 << (key_num))) >> key_num;
>
> No need to shift by key_num, input_report_key normalizes to 'bool'.
>
yes, will change.
>> +     data->statusbits = reg;
>> +     data->key_val = data->keycodes[key_num];
>> +
>> +     input_event(input, EV_MSC, MSC_SCAN, data->key_val);
>
> sScancode is not data->key_val as it is KEY_XXX value but rather key_num.
>
thanks for point out, will change to key_num.

>> +     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(
>
> __devinit
will do.
>
>                            struct mpr121_platform_data *pdata,
>
> pdata should be const.
>
>> +                         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*/
>> +     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",
>> +              data->keycount);
>
> Does it have to be dev_info? dev_dbg maybe?
>
will change to dev_dbg.
>> +
>> +     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;
>
> const.
will do.
>
>> +     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");
>> +             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);
>
> You should do this at the very end, before returning success (0).
> Actually, you do do this, so just remove this instance.
>
will remove.
>> +
>> +     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;
>
> No, you are assigning pointer to the platform data, instead of pointer
> to the data in device structure. That is why I'd rather you renamed
> 'struct mpr121_touchkey_data' to 'struct mpr121_touchkey' and 'data' to
> 'mpr121' so as to avoid confusion with platform data.
>
I've changed all struct mpr121_touchkey_data to struct mpr121_touchkey.
and change the "data" to mpr121.

I'm truly confuse with platform data.

>> +     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");
>
> No need for having this message. You can see the device bound to the
> driver in sysfs. The boot is noisy enough.
ok, will do.
>
>> +     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,
>> +     },
>> +     .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
>> + * 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 */
>> +#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.
>
> Hmm, most of the comments require proper grammar. If you need help with
> it - let the list know, we'll try to help.
>
>> + */
>> +#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`.
>> + * 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  */
>> +#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 */
>> +#define TOUCH_STATUS_MASK            0xfff
>> +/* MPR121 have 12 electrodes */
>> +#define MPR121_MAX_KEY_COUNT         12
>> +
>> +
>> +/**
>> + * @keycount: how many key maped
>> + * @vdd_uv: voltage of vdd supply the chip in uV
>> + * @matrix: maxtrix of keys
>> + * @wakeup: can key wake up system.
>> + */
>> +struct mpr121_platform_data {
>> +     u16 keycount;
>> +     u16 *matrix;
>
> If this device is indeed organized as a matrix (lookslike this) maybe
> you should look at definitions provided by
> include/linux/input/matrix_keypad.h

OK, will change platform data to use matrix_keypad_data structure, I
will convert them into an array into struct mpr121_touchkey: u16
keycodes[12]. The reason is it's will more similar to controller's
register map.

>
>> +     int wakeup;
>
> bool?
wiil do.
>
>> +     int vdd_uv;
>> +};
>> +
>> +#endif
>
> Thanks.
>
> --
> Dmitry
>
--
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