Path: blob/master/drivers/input/touchscreen/da9034-ts.c
15111 views
/*1* Touchscreen driver for Dialog Semiconductor DA90342*3* Copyright (C) 2006-2008 Marvell International Ltd.4* Fengwei Yin <[email protected]>5* Bin Yang <[email protected]>6* Eric Miao <[email protected]>7*8* This program is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License version 2 as10* published by the Free Software Foundation.11*/1213#include <linux/module.h>14#include <linux/kernel.h>15#include <linux/init.h>16#include <linux/delay.h>17#include <linux/platform_device.h>18#include <linux/input.h>19#include <linux/workqueue.h>20#include <linux/mfd/da903x.h>21#include <linux/slab.h>2223#define DA9034_MANUAL_CTRL 0x5024#define DA9034_LDO_ADC_EN (1 << 4)2526#define DA9034_AUTO_CTRL1 0x512728#define DA9034_AUTO_CTRL2 0x5229#define DA9034_AUTO_TSI_EN (1 << 3)30#define DA9034_PEN_DETECT (1 << 4)3132#define DA9034_TSI_CTRL1 0x5333#define DA9034_TSI_CTRL2 0x5434#define DA9034_TSI_X_MSB 0x6c35#define DA9034_TSI_Y_MSB 0x6d36#define DA9034_TSI_XY_LSB 0x6e3738enum {39STATE_IDLE, /* wait for pendown */40STATE_BUSY, /* TSI busy sampling */41STATE_STOP, /* sample available */42STATE_WAIT, /* Wait to start next sample */43};4445enum {46EVENT_PEN_DOWN,47EVENT_PEN_UP,48EVENT_TSI_READY,49EVENT_TIMEDOUT,50};5152struct da9034_touch {53struct device *da9034_dev;54struct input_dev *input_dev;5556struct delayed_work tsi_work;57struct notifier_block notifier;5859int state;6061int interval_ms;62int x_inverted;63int y_inverted;6465int last_x;66int last_y;67};6869static inline int is_pen_down(struct da9034_touch *touch)70{71return da903x_query_status(touch->da9034_dev, DA9034_STATUS_PEN_DOWN);72}7374static inline int detect_pen_down(struct da9034_touch *touch, int on)75{76if (on)77return da903x_set_bits(touch->da9034_dev,78DA9034_AUTO_CTRL2, DA9034_PEN_DETECT);79else80return da903x_clr_bits(touch->da9034_dev,81DA9034_AUTO_CTRL2, DA9034_PEN_DETECT);82}8384static int read_tsi(struct da9034_touch *touch)85{86uint8_t _x, _y, _v;87int ret;8889ret = da903x_read(touch->da9034_dev, DA9034_TSI_X_MSB, &_x);90if (ret)91return ret;9293ret = da903x_read(touch->da9034_dev, DA9034_TSI_Y_MSB, &_y);94if (ret)95return ret;9697ret = da903x_read(touch->da9034_dev, DA9034_TSI_XY_LSB, &_v);98if (ret)99return ret;100101touch->last_x = ((_x << 2) & 0x3fc) | (_v & 0x3);102touch->last_y = ((_y << 2) & 0x3fc) | ((_v & 0xc) >> 2);103104return 0;105}106107static inline int start_tsi(struct da9034_touch *touch)108{109return da903x_set_bits(touch->da9034_dev,110DA9034_AUTO_CTRL2, DA9034_AUTO_TSI_EN);111}112113static inline int stop_tsi(struct da9034_touch *touch)114{115return da903x_clr_bits(touch->da9034_dev,116DA9034_AUTO_CTRL2, DA9034_AUTO_TSI_EN);117}118119static inline void report_pen_down(struct da9034_touch *touch)120{121int x = touch->last_x;122int y = touch->last_y;123124x &= 0xfff;125if (touch->x_inverted)126x = 1024 - x;127y &= 0xfff;128if (touch->y_inverted)129y = 1024 - y;130131input_report_abs(touch->input_dev, ABS_X, x);132input_report_abs(touch->input_dev, ABS_Y, y);133input_report_key(touch->input_dev, BTN_TOUCH, 1);134135input_sync(touch->input_dev);136}137138static inline void report_pen_up(struct da9034_touch *touch)139{140input_report_key(touch->input_dev, BTN_TOUCH, 0);141input_sync(touch->input_dev);142}143144static void da9034_event_handler(struct da9034_touch *touch, int event)145{146int err;147148switch (touch->state) {149case STATE_IDLE:150if (event != EVENT_PEN_DOWN)151break;152153/* Enable auto measurement of the TSI, this will154* automatically disable pen down detection155*/156err = start_tsi(touch);157if (err)158goto err_reset;159160touch->state = STATE_BUSY;161break;162163case STATE_BUSY:164if (event != EVENT_TSI_READY)165break;166167err = read_tsi(touch);168if (err)169goto err_reset;170171/* Disable auto measurement of the TSI, so that172* pen down status will be available173*/174err = stop_tsi(touch);175if (err)176goto err_reset;177178touch->state = STATE_STOP;179180/* FIXME: PEN_{UP/DOWN} events are expected to be181* available by stopping TSI, but this is found not182* always true, delay and simulate such an event183* here is more reliable184*/185mdelay(1);186da9034_event_handler(touch,187is_pen_down(touch) ? EVENT_PEN_DOWN :188EVENT_PEN_UP);189break;190191case STATE_STOP:192if (event == EVENT_PEN_DOWN) {193report_pen_down(touch);194schedule_delayed_work(&touch->tsi_work,195msecs_to_jiffies(touch->interval_ms));196touch->state = STATE_WAIT;197}198199if (event == EVENT_PEN_UP) {200report_pen_up(touch);201touch->state = STATE_IDLE;202}203break;204205case STATE_WAIT:206if (event != EVENT_TIMEDOUT)207break;208209if (is_pen_down(touch)) {210start_tsi(touch);211touch->state = STATE_BUSY;212} else {213report_pen_up(touch);214touch->state = STATE_IDLE;215}216break;217}218return;219220err_reset:221touch->state = STATE_IDLE;222stop_tsi(touch);223detect_pen_down(touch, 1);224}225226static void da9034_tsi_work(struct work_struct *work)227{228struct da9034_touch *touch =229container_of(work, struct da9034_touch, tsi_work.work);230231da9034_event_handler(touch, EVENT_TIMEDOUT);232}233234static int da9034_touch_notifier(struct notifier_block *nb,235unsigned long event, void *data)236{237struct da9034_touch *touch =238container_of(nb, struct da9034_touch, notifier);239240if (event & DA9034_EVENT_TSI_READY)241da9034_event_handler(touch, EVENT_TSI_READY);242243if ((event & DA9034_EVENT_PEN_DOWN) && touch->state == STATE_IDLE)244da9034_event_handler(touch, EVENT_PEN_DOWN);245246return 0;247}248249static int da9034_touch_open(struct input_dev *dev)250{251struct da9034_touch *touch = input_get_drvdata(dev);252int ret;253254ret = da903x_register_notifier(touch->da9034_dev, &touch->notifier,255DA9034_EVENT_PEN_DOWN | DA9034_EVENT_TSI_READY);256if (ret)257return -EBUSY;258259/* Enable ADC LDO */260ret = da903x_set_bits(touch->da9034_dev,261DA9034_MANUAL_CTRL, DA9034_LDO_ADC_EN);262if (ret)263return ret;264265/* TSI_DELAY: 3 slots, TSI_SKIP: 3 slots */266ret = da903x_write(touch->da9034_dev, DA9034_TSI_CTRL1, 0x1b);267if (ret)268return ret;269270ret = da903x_write(touch->da9034_dev, DA9034_TSI_CTRL2, 0x00);271if (ret)272return ret;273274touch->state = STATE_IDLE;275detect_pen_down(touch, 1);276277return 0;278}279280static void da9034_touch_close(struct input_dev *dev)281{282struct da9034_touch *touch = input_get_drvdata(dev);283284da903x_unregister_notifier(touch->da9034_dev, &touch->notifier,285DA9034_EVENT_PEN_DOWN | DA9034_EVENT_TSI_READY);286287cancel_delayed_work_sync(&touch->tsi_work);288289touch->state = STATE_IDLE;290stop_tsi(touch);291detect_pen_down(touch, 0);292293/* Disable ADC LDO */294da903x_clr_bits(touch->da9034_dev,295DA9034_MANUAL_CTRL, DA9034_LDO_ADC_EN);296}297298299static int __devinit da9034_touch_probe(struct platform_device *pdev)300{301struct da9034_touch_pdata *pdata = pdev->dev.platform_data;302struct da9034_touch *touch;303struct input_dev *input_dev;304int ret;305306touch = kzalloc(sizeof(struct da9034_touch), GFP_KERNEL);307if (touch == NULL) {308dev_err(&pdev->dev, "failed to allocate driver data\n");309return -ENOMEM;310}311312touch->da9034_dev = pdev->dev.parent;313314if (pdata) {315touch->interval_ms = pdata->interval_ms;316touch->x_inverted = pdata->x_inverted;317touch->y_inverted = pdata->y_inverted;318} else319/* fallback into default */320touch->interval_ms = 10;321322INIT_DELAYED_WORK(&touch->tsi_work, da9034_tsi_work);323touch->notifier.notifier_call = da9034_touch_notifier;324325input_dev = input_allocate_device();326if (!input_dev) {327dev_err(&pdev->dev, "failed to allocate input device\n");328ret = -ENOMEM;329goto err_free_touch;330}331332input_dev->name = pdev->name;333input_dev->open = da9034_touch_open;334input_dev->close = da9034_touch_close;335input_dev->dev.parent = &pdev->dev;336337__set_bit(EV_ABS, input_dev->evbit);338__set_bit(ABS_X, input_dev->absbit);339__set_bit(ABS_Y, input_dev->absbit);340input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0);341input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0);342343__set_bit(EV_KEY, input_dev->evbit);344__set_bit(BTN_TOUCH, input_dev->keybit);345346touch->input_dev = input_dev;347input_set_drvdata(input_dev, touch);348349ret = input_register_device(input_dev);350if (ret)351goto err_free_input;352353platform_set_drvdata(pdev, touch);354return 0;355356err_free_input:357input_free_device(input_dev);358err_free_touch:359kfree(touch);360return ret;361}362363static int __devexit da9034_touch_remove(struct platform_device *pdev)364{365struct da9034_touch *touch = platform_get_drvdata(pdev);366367input_unregister_device(touch->input_dev);368kfree(touch);369370return 0;371}372373static struct platform_driver da9034_touch_driver = {374.driver = {375.name = "da9034-touch",376.owner = THIS_MODULE,377},378.probe = da9034_touch_probe,379.remove = __devexit_p(da9034_touch_remove),380};381382static int __init da9034_touch_init(void)383{384return platform_driver_register(&da9034_touch_driver);385}386module_init(da9034_touch_init);387388static void __exit da9034_touch_exit(void)389{390platform_driver_unregister(&da9034_touch_driver);391}392module_exit(da9034_touch_exit);393394MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9034");395MODULE_AUTHOR("Eric Miao <[email protected]>, Bin Yang <[email protected]>");396MODULE_LICENSE("GPL");397MODULE_ALIAS("platform:da9034-touch");398399400