lkml.org 
[lkml]   [2010]   [Jun]   [11]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 3/3] input: STMPE811 touch controller support
    Date
    This one adds a driver for STMPE811 4-wire resistive touchscreen
    controller. STMPE811 is a multifunction device. Hence this driver
    depends on stmpe811_core driver for core functionalities.

    Signed-off-by: Luotao Fu <l.fu@pengutronix.de>
    ---
    drivers/input/touchscreen/Kconfig | 10 +
    drivers/input/touchscreen/Makefile | 1 +
    drivers/input/touchscreen/stmpe811_ts.c | 403 +++++++++++++++++++++++++++++++
    3 files changed, 414 insertions(+), 0 deletions(-)
    create mode 100644 drivers/input/touchscreen/stmpe811_ts.c

    diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
    index 3b9d5e2..059b82b 100644
    --- a/drivers/input/touchscreen/Kconfig
    +++ b/drivers/input/touchscreen/Kconfig
    @@ -603,4 +603,14 @@ config TOUCHSCREEN_TPS6507X
    To compile this driver as a module, choose M here: the
    module will be called tps6507x_ts.

    +config TOUCHSCREEN_STMPE811
    + tristate "STMicroelectronics STMPE811 touchscreen"
    + depends on MFD_STMPE811
    + help
    + Say Y here if you want support for STMicroelectronics
    + STMPE811 based touchscreen controller.
    +
    + To compile this driver as a module, choose M here: the
    + module will be called stmpe811_ts.
    +
    endif
    diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
    index 497964a..9da8948 100644
    --- a/drivers/input/touchscreen/Makefile
    +++ b/drivers/input/touchscreen/Makefile
    @@ -47,3 +47,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
    obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o
    obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o
    obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
    +obj-$(CONFIG_TOUCHSCREEN_STMPE811) += stmpe811_ts.o
    diff --git a/drivers/input/touchscreen/stmpe811_ts.c b/drivers/input/touchscreen/stmpe811_ts.c
    new file mode 100644
    index 0000000..3fed0ba
    --- /dev/null
    +++ b/drivers/input/touchscreen/stmpe811_ts.c
    @@ -0,0 +1,403 @@
    +/* STMicroelectronics STMPE811 Touchscreen Driver
    + *
    + * (C) 2010 Luotao Fu <l.fu@pengutronix.de>
    + * All rights reserved.
    + *
    + * This program is free software; you can redistribute it and/or modify it
    + * under the terms of the GNU General Public License as published by the
    + * Free Software Foundation; either version 2 of the License, or (at your
    + * option) any later version.
    + *
    + */
    +
    +#include <linux/kernel.h>
    +#include <linux/module.h>
    +#include <linux/sched.h>
    +#include <linux/interrupt.h>
    +#include <linux/init.h>
    +#include <linux/device.h>
    +#include <linux/platform_device.h>
    +#include <linux/input.h>
    +#include <linux/slab.h>
    +#include <linux/delay.h>
    +
    +#include <linux/mfd/stmpe811.h>
    +
    +#define STMPE811_TSC_CTRL_OP_MOD_XYZ (0<<1)
    +#define STMPE811_TSC_CTRL_OP_MOD_XY (1<<1)
    +#define STMPE811_TSC_CTRL_OP_MOD_X (2<<1)
    +#define STMPE811_TSC_CTRL_OP_MOD_Y (3<<1)
    +#define STMPE811_TSC_CTRL_OP_MOD_Z (4<<1)
    +
    +#define STMPE811_TSC_CTRL_TSC_STA (1<<7)
    +#define STMPE811_TSC_CTRL_TSC_EN (1<<0)
    +
    +#define STMPE811_TS_NAME "stmpe811-ts"
    +#define XY_MASK 0xfff
    +
    +/*
    + * Module parameters
    + */
    +
    +/*
    + * Sample time:
    + *
    + * Set sample_time = 0 for 36 clocks
    + * sample_time = 1 for 44 clocks
    + * sample_time = 2 for 56 clocks
    + * sample_time = 3 for 64 clocks
    + * sample_time = 4 for 80 clocks
    + * sample_time = 5 for 96 clocks
    + * sample_time = 6 for 144 clocks
    + * This one defines ADC converstion time in number of clock.
    + */
    +static int sample_time = 4;
    +module_param(sample_time, int, 0444);
    +MODULE_PARM_DESC(sample_time,
    + "Set ADC conversion time. Default is 4 (80 clocks)");
    +
    +/*
    + * ADC Bit mode:
    + *
    + * Set mod_12b = 0 for 10bit ADC
    + * mod_12b = 1 for 12bit ADC
    + */
    +static int mod_12b = 1;
    +module_param(mod_12b, int, 0444);
    +MODULE_PARM_DESC(mod_12b, "Set ADC Bit mode. Default is 1 (12bit)");
    +
    +/*
    + * reference source:
    + *
    + * Set ref_sel = 0 for internal reference
    + * ref_sel = 1 for external reference
    + */
    +static int ref_sel;
    +module_param(ref_sel, int, 0444);
    +MODULE_PARM_DESC(ref_sel,
    + "Set ADC reference source. Default is 0 (internal reference)");
    +
    +/*
    + * ADC Clock speed:
    + *
    + * Set adc_freq = 0 for 1.625 MHz
    + * adc_freq = 1 for 3.25 MHz
    + * adc_freq = 2 | adc_freq = 3 for 6.5 MHz
    + */
    +static int adc_freq = 1;
    +module_param(adc_freq, int, 0444);
    +MODULE_PARM_DESC(adc_freq, "Set ADC clock speed. Default is 1 (3.25MHz)");
    +
    +/*
    + * Sample average control:
    + *
    + * Set ave_ctrl = 0 for 1 sample
    + * ave_ctrl = 1 for 2 samples
    + * ave_ctrl = 2 for 4 samples
    + * ave_ctrl = 3 for 8 samples
    + */
    +static int ave_ctrl = 3;
    +module_param(ave_ctrl, int, 0444);
    +MODULE_PARM_DESC(adc_freq,
    + "Set average sample counts. Default is 3 (8 samples)");
    +
    +/*
    + * Touch detect interrupt delay:
    + *
    + * Set touch_det_delay = 0 for 10 us
    + * touch_det_delay = 1 for 50 us
    + * touch_det_delay = 2 for 100 us
    + * touch_det_delay = 3 for 500 us
    + * touch_det_delay = 4 for 1 ms
    + * touch_det_delay = 5 for 5 ms
    + * touch_det_delay = 6 for 10 ms
    + * touch_det_delay = 7 for 50 ms
    + * This one defines the delay for signaling a touch detection interrupt after
    + * the actual event
    + */
    +static int touch_det_delay = 3;
    +module_param(touch_det_delay, int, 0444);
    +MODULE_PARM_DESC(touch_det_delay,
    + "Set touch detect delay. Default is 3 (500 us)");
    +
    +/*
    + * Panel driver settling time
    + *
    + * Set settling = 0 for 10 us
    + * settling = 1 for 100 us
    + * settling = 2 for 500 us
    + * settling = 3 for 1 ms
    + * settling = 4 for 5 ms
    + * settling = 5 for 10 ms
    + * settling = 6 for 50 ms
    + * settling = 7 for 100 ms
    + *
    + * This one defines the delay time the ADC shall give the pannel to settle its
    + * voltage for stable measurement.
    + */
    +static int settling = 2;
    +module_param(settling, int, 0444);
    +MODULE_PARM_DESC(settling,
    + "Set panel driver settling time. Default is 2 (500 us)");
    +
    +/*
    + * Length of the fractional part in z
    + *
    + * value coding is quite identical with touch_det_delay above, only
    + * fraction_z ([0..7]) = Count of the fractional part
    + *
    + * This one allows to select range and accuracy of the pressure measurement
    + */
    +static int fraction_z = 7;
    +module_param(fraction_z, int, 0444);
    +MODULE_PARM_DESC(fraction_z,
    + "Set fractional part of z. Default is 7 (7 fractional, 1 whole)");
    +
    +struct stmpe811_touch {
    + struct stmpe811 *stm;
    + struct input_dev *idev;
    + struct delayed_work work;
    +};
    +
    +static void stmpe811_work(struct work_struct *work)
    +{
    + u8 int_sta;
    + u32 timeout = 40;
    +
    + struct stmpe811_touch *ts =
    + container_of(work, struct stmpe811_touch, work.work);
    +
    + stmpe811_reg_read(ts->stm, STMPE811_REG_INT_STA, &int_sta);
    +
    + /* touch_det sometimes get desasserted or just get stuck. This appears
    + * to be a silicon bug, We still have to clearify this with the
    + * manufacture. As a workaround We release the key anyway if the
    + * touch_det keeps coming in after 4ms, while the FIFO contains no value
    + * during the whole time. */
    + while ((int_sta & (1 << STMPE811_IRQ_TOUCH_DET)) && (timeout > 0)) {
    + timeout--;
    + stmpe811_reg_read(ts->stm, STMPE811_REG_INT_STA, &int_sta);
    + udelay(100);
    + }
    +
    + input_report_abs(ts->idev, ABS_PRESSURE, 0);
    + input_sync(ts->idev);
    +}
    +
    +static irqreturn_t stmpe811_ts_handler(int irq, void *data)
    +{
    + u8 data_set[4];
    + int x, y, z;
    + struct stmpe811_touch *ts = data;
    +
    + /* Cancel polling for release if we have new value available. */
    + cancel_delayed_work(&ts->work);
    +
    + /*
    + * The FIFO sometimes just crashes and stops generating interrupts. This
    + * appears to be a silicon bug. We still have to clearify this with
    + * the manufacture. As a workaround we disable the TSC while we are
    + * collecting data and flush the FIFO after reading
    + */
    + stmpe811_reg_clear_bits(ts->stm, STMPE811_REG_TSC_CTRL,
    + STMPE811_TSC_CTRL_TSC_EN);
    +
    + stmpe811_block_read(ts->stm, STMPE811_REG_TSC_DATA_XYZ, 4, data_set);
    +
    + x = (data_set[0] << 4) | (data_set[1] >> 4);
    + y = ((data_set[1] & 0xf) << 8) | data_set[2];
    + z = data_set[3];
    +
    + input_report_abs(ts->idev, ABS_X, x);
    + input_report_abs(ts->idev, ABS_Y, y);
    + input_report_abs(ts->idev, ABS_PRESSURE, z);
    + input_sync(ts->idev);
    +
    + /* flush the FIFO after we have read out our values. */
    + stmpe811_reg_set_bits(ts->stm, STMPE811_REG_FIFO_STA,
    + STMPE811_FIFO_STA_RESET);
    + stmpe811_reg_clear_bits(ts->stm, STMPE811_REG_FIFO_STA,
    + STMPE811_FIFO_STA_RESET);
    +
    + /* reenable the tsc */
    + stmpe811_reg_set_bits(ts->stm, STMPE811_REG_TSC_CTRL,
    + STMPE811_TSC_CTRL_TSC_EN);
    +
    + /* start polling for touch_det to detect release */
    + schedule_delayed_work(&ts->work, HZ / 50);
    +
    + return IRQ_HANDLED;
    +}
    +
    +static int stmpe811_ts_open(struct input_dev *dev)
    +{
    + struct stmpe811_touch *ts = input_get_drvdata(dev);
    +
    + return stmpe811_reg_set_bits(ts->stm, STMPE811_REG_TSC_CTRL,
    + STMPE811_TSC_CTRL_TSC_EN);
    +}
    +
    +static void stmpe811_ts_close(struct input_dev *dev)
    +{
    + struct stmpe811_touch *ts = input_get_drvdata(dev);
    +
    + stmpe811_reg_clear_bits(ts->stm, STMPE811_REG_TSC_CTRL,
    + STMPE811_TSC_CTRL_TSC_EN);
    +}
    +
    +static int __devinit stmpe811_input_probe(struct platform_device *pdev)
    +{
    + struct stmpe811_touch *ts;
    + struct input_dev *idev;
    + int ret = 0;
    +
    + ts = kzalloc(sizeof(*ts), GFP_KERNEL);
    + if (!ts)
    + goto err_out;
    +
    + idev = input_allocate_device();
    + if (!idev)
    + goto err_free_ts;
    +
    + platform_set_drvdata(pdev, ts);
    + ts->stm = dev_get_drvdata(pdev->dev.parent);
    + ts->idev = idev;
    +
    + INIT_DELAYED_WORK(&ts->work, stmpe811_work);
    +
    + stmpe811_register_irq(ts->stm, STMPE811_IRQ_FIFO_TH,
    + stmpe811_ts_handler, ts);
    +
    + ret = stmpe811_reg_clear_bits(ts->stm, STMPE811_REG_SYS_CTRL2,
    + (STMPE811_SYS_CTRL2_ADC_OFF |
    + STMPE811_SYS_CTRL2_TSC_OFF));
    + if (ret) {
    + dev_err(&pdev->dev, "Could not enable clock for ADC and TS\n");
    + goto err_free_plat;
    + }
    +
    + ret = stmpe811_reg_set_bits(ts->stm, STMPE811_REG_ADC_CTRL1,
    + (sample_time << 4) | (mod_12b << 3) | (ref_sel << 1));
    + if (ret) {
    + dev_err(&pdev->dev, "Could not setup ADC\n");
    + goto err_free_plat;
    + }
    +
    + ret = stmpe811_reg_set_bits(ts->stm, STMPE811_REG_ADC_CTRL2, adc_freq);
    + if (ret) {
    + dev_err(&pdev->dev, "Could not setup ADC\n");
    + goto err_free_plat;
    + }
    +
    + ret = stmpe811_reg_set_bits(ts->stm, STMPE811_REG_TSC_CFG,
    + (ave_ctrl << 6) | (touch_det_delay << 3) | settling);
    + if (ret) {
    + dev_err(&pdev->dev, "Could not config touch\n");
    + goto err_free_plat;
    + }
    +
    + ret = stmpe811_reg_set_bits(ts->stm,
    + STMPE811_REG_TSC_FRACTION_Z, fraction_z);
    + if (ret) {
    + dev_err(&pdev->dev, "Could not config touch\n");
    + goto err_free_plat;
    + }
    +
    + /* set FIFO to 1 for single point reading */
    + ret = stmpe811_reg_write(ts->stm, STMPE811_REG_FIFO_TH, 1);
    + if (ret) {
    + dev_err(&pdev->dev, "Could not set FIFO\n");
    + goto err_free_plat;
    + }
    +
    + ret = stmpe811_reg_set_bits(ts->stm, STMPE811_REG_TSC_CTRL,
    + STMPE811_TSC_CTRL_OP_MOD_XYZ);
    + if (ret) {
    + dev_err(&pdev->dev, "Could not set mode\n");
    + goto err_free_plat;
    + }
    +
    + idev->name = STMPE811_TS_NAME;
    + idev->id.bustype = BUS_I2C;
    + idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
    + idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
    +
    + idev->open = stmpe811_ts_open;
    + idev->close = stmpe811_ts_close;
    +
    + input_set_drvdata(idev, ts);
    +
    + ret = input_register_device(idev);
    + if (ret) {
    + dev_err(&pdev->dev, "Could not register input device\n");
    + goto err_free_plat;
    + }
    +
    + ts->stm->active_flag |= STMPE811_USE_TS;
    +
    + input_set_abs_params(idev, ABS_X, 0, XY_MASK, 0, 0);
    + input_set_abs_params(idev, ABS_Y, 0, XY_MASK, 0, 0);
    + input_set_abs_params(idev, ABS_PRESSURE, 0x0, 0xff, 0, 0);
    +
    + return ret;
    +
    +err_free_plat:
    + platform_set_drvdata(pdev, NULL);
    + input_free_device(idev);
    +err_free_ts:
    + kfree(ts);
    +err_out:
    + return ret;
    +}
    +
    +static int __devexit stmpe811_ts_remove(struct platform_device *pdev)
    +{
    + struct stmpe811_touch *ts = platform_get_drvdata(pdev);
    +
    + cancel_delayed_work(&ts->work);
    +
    + stmpe811_free_irq(ts->stm, STMPE811_IRQ_FIFO_TH);
    + /*disable FIFO TH */
    + stmpe811_reg_write(ts->stm, STMPE811_REG_FIFO_TH, 0);
    +
    + stmpe811_reg_set_bits(ts->stm, STMPE811_REG_SYS_CTRL2,
    + (STMPE811_SYS_CTRL2_ADC_OFF |
    + STMPE811_SYS_CTRL2_ADC_OFF));
    +
    + ts->stm->active_flag &= ~STMPE811_USE_TS;
    + platform_set_drvdata(pdev, NULL);
    +
    + input_unregister_device(ts->idev);
    + input_free_device(ts->idev);
    +
    + kfree(ts);
    +
    + return 0;
    +}
    +
    +static struct platform_driver stmpe811_input_driver = {
    + .driver = {
    + .name = "stmpe811-ts",
    + },
    + .probe = stmpe811_input_probe,
    + .remove = __devexit_p(stmpe811_ts_remove),
    +};
    +
    +static int __init stmpe811_input_init(void)
    +{
    + return platform_driver_register(&stmpe811_input_driver);
    +}
    +
    +module_init(stmpe811_input_init);
    +
    +static void __exit stmpe811_input_exit(void)
    +{
    + platform_driver_unregister(&stmpe811_input_driver);
    +}
    +
    +module_exit(stmpe811_input_exit);
    +
    +MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
    +MODULE_DESCRIPTION("STMPE811 touchscreen driver");
    +MODULE_LICENSE("GPL");
    +MODULE_ALIAS("platform:" STMPE811_TS_NAME);
    --
    1.7.1


    \
     
     \ /
      Last update: 2010-06-11 12:17    [W:0.043 / U:60.264 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site