Path: blob/master/drivers/input/touchscreen/pcap_ts.c
15112 views
/*1* Driver for Motorola PCAP2 touchscreen as found in the EZX phone platform.2*3* Copyright (C) 2006 Harald Welte <[email protected]>4* Copyright (C) 2009 Daniel Ribeiro <[email protected]>5*6* This program is free software; you can redistribute it and/or modify7* it under the terms of the GNU General Public License version 2 as8* published by the Free Software Foundation.9*10*/1112#include <linux/module.h>13#include <linux/init.h>14#include <linux/fs.h>15#include <linux/string.h>16#include <linux/slab.h>17#include <linux/pm.h>18#include <linux/timer.h>19#include <linux/interrupt.h>20#include <linux/platform_device.h>21#include <linux/input.h>22#include <linux/mfd/ezx-pcap.h>2324struct pcap_ts {25struct pcap_chip *pcap;26struct input_dev *input;27struct delayed_work work;28u16 x, y;29u16 pressure;30u8 read_state;31};3233#define SAMPLE_DELAY 20 /* msecs */3435#define X_AXIS_MIN 036#define X_AXIS_MAX 102337#define Y_AXIS_MAX X_AXIS_MAX38#define Y_AXIS_MIN X_AXIS_MIN39#define PRESSURE_MAX X_AXIS_MAX40#define PRESSURE_MIN X_AXIS_MIN4142static void pcap_ts_read_xy(void *data, u16 res[2])43{44struct pcap_ts *pcap_ts = data;4546switch (pcap_ts->read_state) {47case PCAP_ADC_TS_M_PRESSURE:48/* pressure reading is unreliable */49if (res[0] > PRESSURE_MIN && res[0] < PRESSURE_MAX)50pcap_ts->pressure = res[0];51pcap_ts->read_state = PCAP_ADC_TS_M_XY;52schedule_delayed_work(&pcap_ts->work, 0);53break;54case PCAP_ADC_TS_M_XY:55pcap_ts->y = res[0];56pcap_ts->x = res[1];57if (pcap_ts->x <= X_AXIS_MIN || pcap_ts->x >= X_AXIS_MAX ||58pcap_ts->y <= Y_AXIS_MIN || pcap_ts->y >= Y_AXIS_MAX) {59/* pen has been released */60input_report_abs(pcap_ts->input, ABS_PRESSURE, 0);61input_report_key(pcap_ts->input, BTN_TOUCH, 0);6263pcap_ts->read_state = PCAP_ADC_TS_M_STANDBY;64schedule_delayed_work(&pcap_ts->work, 0);65} else {66/* pen is touching the screen */67input_report_abs(pcap_ts->input, ABS_X, pcap_ts->x);68input_report_abs(pcap_ts->input, ABS_Y, pcap_ts->y);69input_report_key(pcap_ts->input, BTN_TOUCH, 1);70input_report_abs(pcap_ts->input, ABS_PRESSURE,71pcap_ts->pressure);7273/* switch back to pressure read mode */74pcap_ts->read_state = PCAP_ADC_TS_M_PRESSURE;75schedule_delayed_work(&pcap_ts->work,76msecs_to_jiffies(SAMPLE_DELAY));77}78input_sync(pcap_ts->input);79break;80default:81dev_warn(&pcap_ts->input->dev,82"pcap_ts: Warning, unhandled read_state %d\n",83pcap_ts->read_state);84break;85}86}8788static void pcap_ts_work(struct work_struct *work)89{90struct delayed_work *dw = container_of(work, struct delayed_work, work);91struct pcap_ts *pcap_ts = container_of(dw, struct pcap_ts, work);92u8 ch[2];9394pcap_set_ts_bits(pcap_ts->pcap,95pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);9697if (pcap_ts->read_state == PCAP_ADC_TS_M_STANDBY)98return;99100/* start adc conversion */101ch[0] = PCAP_ADC_CH_TS_X1;102ch[1] = PCAP_ADC_CH_TS_Y1;103pcap_adc_async(pcap_ts->pcap, PCAP_ADC_BANK_1, 0, ch,104pcap_ts_read_xy, pcap_ts);105}106107static irqreturn_t pcap_ts_event_touch(int pirq, void *data)108{109struct pcap_ts *pcap_ts = data;110111if (pcap_ts->read_state == PCAP_ADC_TS_M_STANDBY) {112pcap_ts->read_state = PCAP_ADC_TS_M_PRESSURE;113schedule_delayed_work(&pcap_ts->work, 0);114}115return IRQ_HANDLED;116}117118static int pcap_ts_open(struct input_dev *dev)119{120struct pcap_ts *pcap_ts = input_get_drvdata(dev);121122pcap_ts->read_state = PCAP_ADC_TS_M_STANDBY;123schedule_delayed_work(&pcap_ts->work, 0);124125return 0;126}127128static void pcap_ts_close(struct input_dev *dev)129{130struct pcap_ts *pcap_ts = input_get_drvdata(dev);131132cancel_delayed_work_sync(&pcap_ts->work);133134pcap_ts->read_state = PCAP_ADC_TS_M_NONTS;135pcap_set_ts_bits(pcap_ts->pcap,136pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);137}138139static int __devinit pcap_ts_probe(struct platform_device *pdev)140{141struct input_dev *input_dev;142struct pcap_ts *pcap_ts;143int err = -ENOMEM;144145pcap_ts = kzalloc(sizeof(*pcap_ts), GFP_KERNEL);146if (!pcap_ts)147return err;148149pcap_ts->pcap = dev_get_drvdata(pdev->dev.parent);150platform_set_drvdata(pdev, pcap_ts);151152input_dev = input_allocate_device();153if (!input_dev)154goto fail;155156INIT_DELAYED_WORK(&pcap_ts->work, pcap_ts_work);157158pcap_ts->read_state = PCAP_ADC_TS_M_NONTS;159pcap_set_ts_bits(pcap_ts->pcap,160pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);161162pcap_ts->input = input_dev;163input_set_drvdata(input_dev, pcap_ts);164165input_dev->name = "pcap-touchscreen";166input_dev->phys = "pcap_ts/input0";167input_dev->id.bustype = BUS_HOST;168input_dev->id.vendor = 0x0001;169input_dev->id.product = 0x0002;170input_dev->id.version = 0x0100;171input_dev->dev.parent = &pdev->dev;172input_dev->open = pcap_ts_open;173input_dev->close = pcap_ts_close;174175input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);176input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);177input_set_abs_params(input_dev, ABS_X, X_AXIS_MIN, X_AXIS_MAX, 0, 0);178input_set_abs_params(input_dev, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, 0, 0);179input_set_abs_params(input_dev, ABS_PRESSURE, PRESSURE_MIN,180PRESSURE_MAX, 0, 0);181182err = input_register_device(pcap_ts->input);183if (err)184goto fail_allocate;185186err = request_irq(pcap_to_irq(pcap_ts->pcap, PCAP_IRQ_TS),187pcap_ts_event_touch, 0, "Touch Screen", pcap_ts);188if (err)189goto fail_register;190191return 0;192193fail_register:194input_unregister_device(input_dev);195goto fail;196fail_allocate:197input_free_device(input_dev);198fail:199kfree(pcap_ts);200201return err;202}203204static int __devexit pcap_ts_remove(struct platform_device *pdev)205{206struct pcap_ts *pcap_ts = platform_get_drvdata(pdev);207208free_irq(pcap_to_irq(pcap_ts->pcap, PCAP_IRQ_TS), pcap_ts);209cancel_delayed_work_sync(&pcap_ts->work);210211input_unregister_device(pcap_ts->input);212213kfree(pcap_ts);214215return 0;216}217218#ifdef CONFIG_PM219static int pcap_ts_suspend(struct device *dev)220{221struct pcap_ts *pcap_ts = dev_get_drvdata(dev);222223pcap_set_ts_bits(pcap_ts->pcap, PCAP_ADC_TS_REF_LOWPWR);224return 0;225}226227static int pcap_ts_resume(struct device *dev)228{229struct pcap_ts *pcap_ts = dev_get_drvdata(dev);230231pcap_set_ts_bits(pcap_ts->pcap,232pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);233return 0;234}235236static const struct dev_pm_ops pcap_ts_pm_ops = {237.suspend = pcap_ts_suspend,238.resume = pcap_ts_resume,239};240#define PCAP_TS_PM_OPS (&pcap_ts_pm_ops)241#else242#define PCAP_TS_PM_OPS NULL243#endif244245static struct platform_driver pcap_ts_driver = {246.probe = pcap_ts_probe,247.remove = __devexit_p(pcap_ts_remove),248.driver = {249.name = "pcap-ts",250.owner = THIS_MODULE,251.pm = PCAP_TS_PM_OPS,252},253};254255static int __init pcap_ts_init(void)256{257return platform_driver_register(&pcap_ts_driver);258}259260static void __exit pcap_ts_exit(void)261{262platform_driver_unregister(&pcap_ts_driver);263}264265module_init(pcap_ts_init);266module_exit(pcap_ts_exit);267268MODULE_DESCRIPTION("Motorola PCAP2 touchscreen driver");269MODULE_AUTHOR("Daniel Ribeiro / Harald Welte");270MODULE_LICENSE("GPL");271MODULE_ALIAS("platform:pcap_ts");272273274