Path: blob/master/drivers/input/touchscreen/tsc2007.c
15109 views
/*1* drivers/input/touchscreen/tsc2007.c2*3* Copyright (c) 2008 MtekVision Co., Ltd.4* Kwangwoo Lee <[email protected]>5*6* Using code from:7* - ads7846.c8* Copyright (c) 2005 David Brownell9* Copyright (c) 2006 Nokia Corporation10* - corgi_ts.c11* Copyright (C) 2004-2005 Richard Purdie12* - omap_ts.[hc], ads7846.h, ts_osk.c13* Copyright (C) 2002 MontaVista Software14* Copyright (C) 2004 Texas Instruments15* Copyright (C) 2005 Dirk Behme16*17* This program is free software; you can redistribute it and/or modify18* it under the terms of the GNU General Public License version 2 as19* published by the Free Software Foundation.20*/2122#include <linux/module.h>23#include <linux/slab.h>24#include <linux/input.h>25#include <linux/interrupt.h>26#include <linux/i2c.h>27#include <linux/i2c/tsc2007.h>2829#define TSC2007_MEASURE_TEMP0 (0x0 << 4)30#define TSC2007_MEASURE_AUX (0x2 << 4)31#define TSC2007_MEASURE_TEMP1 (0x4 << 4)32#define TSC2007_ACTIVATE_XN (0x8 << 4)33#define TSC2007_ACTIVATE_YN (0x9 << 4)34#define TSC2007_ACTIVATE_YP_XN (0xa << 4)35#define TSC2007_SETUP (0xb << 4)36#define TSC2007_MEASURE_X (0xc << 4)37#define TSC2007_MEASURE_Y (0xd << 4)38#define TSC2007_MEASURE_Z1 (0xe << 4)39#define TSC2007_MEASURE_Z2 (0xf << 4)4041#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2)42#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2)43#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2)44#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2)4546#define TSC2007_12BIT (0x0 << 1)47#define TSC2007_8BIT (0x1 << 1)4849#define MAX_12BIT ((1 << 12) - 1)5051#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)5253#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y)54#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1)55#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2)56#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X)57#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)5859struct ts_event {60u16 x;61u16 y;62u16 z1, z2;63};6465struct tsc2007 {66struct input_dev *input;67char phys[32];68struct delayed_work work;6970struct i2c_client *client;7172u16 model;73u16 x_plate_ohms;74u16 max_rt;75unsigned long poll_delay;76unsigned long poll_period;7778bool pendown;79int irq;8081int (*get_pendown_state)(void);82void (*clear_penirq)(void);83};8485static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)86{87s32 data;88u16 val;8990data = i2c_smbus_read_word_data(tsc->client, cmd);91if (data < 0) {92dev_err(&tsc->client->dev, "i2c io error: %d\n", data);93return data;94}9596/* The protocol and raw data format from i2c interface:97* S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P98* Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit].99*/100val = swab16(data) >> 4;101102dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val);103104return val;105}106107static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)108{109/* y- still on; turn on only y+ (and ADC) */110tc->y = tsc2007_xfer(tsc, READ_Y);111112/* turn y- off, x+ on, then leave in lowpower */113tc->x = tsc2007_xfer(tsc, READ_X);114115/* turn y+ off, x- on; we'll use formula #1 */116tc->z1 = tsc2007_xfer(tsc, READ_Z1);117tc->z2 = tsc2007_xfer(tsc, READ_Z2);118119/* Prepare for next touch reading - power down ADC, enable PENIRQ */120tsc2007_xfer(tsc, PWRDOWN);121}122123static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)124{125u32 rt = 0;126127/* range filtering */128if (tc->x == MAX_12BIT)129tc->x = 0;130131if (likely(tc->x && tc->z1)) {132/* compute touch pressure resistance using equation #1 */133rt = tc->z2 - tc->z1;134rt *= tc->x;135rt *= tsc->x_plate_ohms;136rt /= tc->z1;137rt = (rt + 2047) >> 12;138}139140return rt;141}142143static void tsc2007_send_up_event(struct tsc2007 *tsc)144{145struct input_dev *input = tsc->input;146147dev_dbg(&tsc->client->dev, "UP\n");148149input_report_key(input, BTN_TOUCH, 0);150input_report_abs(input, ABS_PRESSURE, 0);151input_sync(input);152}153154static void tsc2007_work(struct work_struct *work)155{156struct tsc2007 *ts =157container_of(to_delayed_work(work), struct tsc2007, work);158bool debounced = false;159struct ts_event tc;160u32 rt;161162/*163* NOTE: We can't rely on the pressure to determine the pen down164* state, even though this controller has a pressure sensor.165* The pressure value can fluctuate for quite a while after166* lifting the pen and in some cases may not even settle at the167* expected value.168*169* The only safe way to check for the pen up condition is in the170* work function by reading the pen signal state (it's a GPIO171* and IRQ). Unfortunately such callback is not always available,172* in that case we have rely on the pressure anyway.173*/174if (ts->get_pendown_state) {175if (unlikely(!ts->get_pendown_state())) {176tsc2007_send_up_event(ts);177ts->pendown = false;178goto out;179}180181dev_dbg(&ts->client->dev, "pen is still down\n");182}183184tsc2007_read_values(ts, &tc);185186rt = tsc2007_calculate_pressure(ts, &tc);187if (rt > ts->max_rt) {188/*189* Sample found inconsistent by debouncing or pressure is190* beyond the maximum. Don't report it to user space,191* repeat at least once more the measurement.192*/193dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);194debounced = true;195goto out;196197}198199if (rt) {200struct input_dev *input = ts->input;201202if (!ts->pendown) {203dev_dbg(&ts->client->dev, "DOWN\n");204205input_report_key(input, BTN_TOUCH, 1);206ts->pendown = true;207}208209input_report_abs(input, ABS_X, tc.x);210input_report_abs(input, ABS_Y, tc.y);211input_report_abs(input, ABS_PRESSURE, rt);212213input_sync(input);214215dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n",216tc.x, tc.y, rt);217218} else if (!ts->get_pendown_state && ts->pendown) {219/*220* We don't have callback to check pendown state, so we221* have to assume that since pressure reported is 0 the222* pen was lifted up.223*/224tsc2007_send_up_event(ts);225ts->pendown = false;226}227228out:229if (ts->pendown || debounced)230schedule_delayed_work(&ts->work,231msecs_to_jiffies(ts->poll_period));232else233enable_irq(ts->irq);234}235236static irqreturn_t tsc2007_irq(int irq, void *handle)237{238struct tsc2007 *ts = handle;239240if (!ts->get_pendown_state || likely(ts->get_pendown_state())) {241disable_irq_nosync(ts->irq);242schedule_delayed_work(&ts->work,243msecs_to_jiffies(ts->poll_delay));244}245246if (ts->clear_penirq)247ts->clear_penirq();248249return IRQ_HANDLED;250}251252static void tsc2007_free_irq(struct tsc2007 *ts)253{254free_irq(ts->irq, ts);255if (cancel_delayed_work_sync(&ts->work)) {256/*257* Work was pending, therefore we need to enable258* IRQ here to balance the disable_irq() done in the259* interrupt handler.260*/261enable_irq(ts->irq);262}263}264265static int __devinit tsc2007_probe(struct i2c_client *client,266const struct i2c_device_id *id)267{268struct tsc2007 *ts;269struct tsc2007_platform_data *pdata = client->dev.platform_data;270struct input_dev *input_dev;271int err;272273if (!pdata) {274dev_err(&client->dev, "platform data is required!\n");275return -EINVAL;276}277278if (!i2c_check_functionality(client->adapter,279I2C_FUNC_SMBUS_READ_WORD_DATA))280return -EIO;281282ts = kzalloc(sizeof(struct tsc2007), GFP_KERNEL);283input_dev = input_allocate_device();284if (!ts || !input_dev) {285err = -ENOMEM;286goto err_free_mem;287}288289ts->client = client;290ts->irq = client->irq;291ts->input = input_dev;292INIT_DELAYED_WORK(&ts->work, tsc2007_work);293294ts->model = pdata->model;295ts->x_plate_ohms = pdata->x_plate_ohms;296ts->max_rt = pdata->max_rt ? : MAX_12BIT;297ts->poll_delay = pdata->poll_delay ? : 1;298ts->poll_period = pdata->poll_period ? : 1;299ts->get_pendown_state = pdata->get_pendown_state;300ts->clear_penirq = pdata->clear_penirq;301302snprintf(ts->phys, sizeof(ts->phys),303"%s/input0", dev_name(&client->dev));304305input_dev->name = "TSC2007 Touchscreen";306input_dev->phys = ts->phys;307input_dev->id.bustype = BUS_I2C;308309input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);310input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);311312input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, pdata->fuzzx, 0);313input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, pdata->fuzzy, 0);314input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT,315pdata->fuzzz, 0);316317if (pdata->init_platform_hw)318pdata->init_platform_hw();319320err = request_irq(ts->irq, tsc2007_irq, 0,321client->dev.driver->name, ts);322if (err < 0) {323dev_err(&client->dev, "irq %d busy?\n", ts->irq);324goto err_free_mem;325}326327/* Prepare for touch readings - power down ADC and enable PENIRQ */328err = tsc2007_xfer(ts, PWRDOWN);329if (err < 0)330goto err_free_irq;331332err = input_register_device(input_dev);333if (err)334goto err_free_irq;335336i2c_set_clientdata(client, ts);337338return 0;339340err_free_irq:341tsc2007_free_irq(ts);342if (pdata->exit_platform_hw)343pdata->exit_platform_hw();344err_free_mem:345input_free_device(input_dev);346kfree(ts);347return err;348}349350static int __devexit tsc2007_remove(struct i2c_client *client)351{352struct tsc2007 *ts = i2c_get_clientdata(client);353struct tsc2007_platform_data *pdata = client->dev.platform_data;354355tsc2007_free_irq(ts);356357if (pdata->exit_platform_hw)358pdata->exit_platform_hw();359360input_unregister_device(ts->input);361kfree(ts);362363return 0;364}365366static const struct i2c_device_id tsc2007_idtable[] = {367{ "tsc2007", 0 },368{ }369};370371MODULE_DEVICE_TABLE(i2c, tsc2007_idtable);372373static struct i2c_driver tsc2007_driver = {374.driver = {375.owner = THIS_MODULE,376.name = "tsc2007"377},378.id_table = tsc2007_idtable,379.probe = tsc2007_probe,380.remove = __devexit_p(tsc2007_remove),381};382383static int __init tsc2007_init(void)384{385return i2c_add_driver(&tsc2007_driver);386}387388static void __exit tsc2007_exit(void)389{390i2c_del_driver(&tsc2007_driver);391}392393module_init(tsc2007_init);394module_exit(tsc2007_exit);395396MODULE_AUTHOR("Kwangwoo Lee <[email protected]>");397MODULE_DESCRIPTION("TSC2007 TouchScreen Driver");398MODULE_LICENSE("GPL");399400401