Path: blob/master/drivers/input/touchscreen/cy8ctmg110_ts.c
15111 views
/*1* Driver for cypress touch screen controller2*3* Copyright (c) 2009 Aava Mobile4*5* Some cleanups by Alan Cox <[email protected]>6*7* This program is free software; you can redistribute it and/or modify8* it under the terms of the GNU General Public License version 2 as9* published by the Free Software Foundation.10*11* This program is distributed in the hope that it will be useful,12* but WITHOUT ANY WARRANTY; without even the implied warranty of13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the14* GNU General Public License for more details.15*16* You should have received a copy of the GNU General Public License17* along with this program; if not, write to the Free Software18* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.19*/2021#include <linux/module.h>22#include <linux/kernel.h>23#include <linux/input.h>24#include <linux/slab.h>25#include <linux/interrupt.h>26#include <linux/io.h>27#include <linux/i2c.h>28#include <linux/gpio.h>29#include <linux/input/cy8ctmg110_pdata.h>3031#define CY8CTMG110_DRIVER_NAME "cy8ctmg110"3233/* Touch coordinates */34#define CY8CTMG110_X_MIN 035#define CY8CTMG110_Y_MIN 036#define CY8CTMG110_X_MAX 75937#define CY8CTMG110_Y_MAX 465383940/* cy8ctmg110 register definitions */41#define CY8CTMG110_TOUCH_WAKEUP_TIME 042#define CY8CTMG110_TOUCH_SLEEP_TIME 243#define CY8CTMG110_TOUCH_X1 344#define CY8CTMG110_TOUCH_Y1 545#define CY8CTMG110_TOUCH_X2 746#define CY8CTMG110_TOUCH_Y2 947#define CY8CTMG110_FINGERS 1148#define CY8CTMG110_GESTURE 1249#define CY8CTMG110_REG_MAX 13505152/*53* The touch driver structure.54*/55struct cy8ctmg110 {56struct input_dev *input;57char phys[32];58struct i2c_client *client;59int reset_pin;60int irq_pin;61};6263/*64* cy8ctmg110_power is the routine that is called when touch hardware65* will powered off or on.66*/67static void cy8ctmg110_power(struct cy8ctmg110 *ts, bool poweron)68{69if (ts->reset_pin)70gpio_direction_output(ts->reset_pin, 1 - poweron);71}7273static int cy8ctmg110_write_regs(struct cy8ctmg110 *tsc, unsigned char reg,74unsigned char len, unsigned char *value)75{76struct i2c_client *client = tsc->client;77int ret;78unsigned char i2c_data[6];7980BUG_ON(len > 5);8182i2c_data[0] = reg;83memcpy(i2c_data + 1, value, len);8485ret = i2c_master_send(client, i2c_data, len + 1);86if (ret != 1) {87dev_err(&client->dev, "i2c write data cmd failed\n");88return ret ? ret : -EIO;89}9091return 0;92}9394static int cy8ctmg110_read_regs(struct cy8ctmg110 *tsc,95unsigned char *data, unsigned char len, unsigned char cmd)96{97struct i2c_client *client = tsc->client;98int ret;99struct i2c_msg msg[2] = {100/* first write slave position to i2c devices */101{ client->addr, 0, 1, &cmd },102/* Second read data from position */103{ client->addr, I2C_M_RD, len, data }104};105106ret = i2c_transfer(client->adapter, msg, 2);107if (ret < 0)108return ret;109110return 0;111}112113static int cy8ctmg110_touch_pos(struct cy8ctmg110 *tsc)114{115struct input_dev *input = tsc->input;116unsigned char reg_p[CY8CTMG110_REG_MAX];117int x, y;118119memset(reg_p, 0, CY8CTMG110_REG_MAX);120121/* Reading coordinates */122if (cy8ctmg110_read_regs(tsc, reg_p, 9, CY8CTMG110_TOUCH_X1) != 0)123return -EIO;124125y = reg_p[2] << 8 | reg_p[3];126x = reg_p[0] << 8 | reg_p[1];127128/* Number of touch */129if (reg_p[8] == 0) {130input_report_key(input, BTN_TOUCH, 0);131} else {132input_report_key(input, BTN_TOUCH, 1);133input_report_abs(input, ABS_X, x);134input_report_abs(input, ABS_Y, y);135}136137input_sync(input);138139return 0;140}141142static int cy8ctmg110_set_sleepmode(struct cy8ctmg110 *ts, bool sleep)143{144unsigned char reg_p[3];145146if (sleep) {147reg_p[0] = 0x00;148reg_p[1] = 0xff;149reg_p[2] = 5;150} else {151reg_p[0] = 0x10;152reg_p[1] = 0xff;153reg_p[2] = 0;154}155156return cy8ctmg110_write_regs(ts, CY8CTMG110_TOUCH_WAKEUP_TIME, 3, reg_p);157}158159static irqreturn_t cy8ctmg110_irq_thread(int irq, void *dev_id)160{161struct cy8ctmg110 *tsc = dev_id;162163cy8ctmg110_touch_pos(tsc);164165return IRQ_HANDLED;166}167168static int __devinit cy8ctmg110_probe(struct i2c_client *client,169const struct i2c_device_id *id)170{171const struct cy8ctmg110_pdata *pdata = client->dev.platform_data;172struct cy8ctmg110 *ts;173struct input_dev *input_dev;174int err;175176/* No pdata no way forward */177if (pdata == NULL) {178dev_err(&client->dev, "no pdata\n");179return -ENODEV;180}181182if (!i2c_check_functionality(client->adapter,183I2C_FUNC_SMBUS_READ_WORD_DATA))184return -EIO;185186ts = kzalloc(sizeof(struct cy8ctmg110), GFP_KERNEL);187input_dev = input_allocate_device();188if (!ts || !input_dev) {189err = -ENOMEM;190goto err_free_mem;191}192193ts->client = client;194ts->input = input_dev;195196snprintf(ts->phys, sizeof(ts->phys),197"%s/input0", dev_name(&client->dev));198199input_dev->name = CY8CTMG110_DRIVER_NAME " Touchscreen";200input_dev->phys = ts->phys;201input_dev->id.bustype = BUS_I2C;202input_dev->dev.parent = &client->dev;203204input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);205input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);206207input_set_abs_params(input_dev, ABS_X,208CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 4, 0);209input_set_abs_params(input_dev, ABS_Y,210CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 4, 0);211212if (ts->reset_pin) {213err = gpio_request(ts->reset_pin, NULL);214if (err) {215dev_err(&client->dev,216"Unable to request GPIO pin %d.\n",217ts->reset_pin);218goto err_free_mem;219}220}221222cy8ctmg110_power(ts, true);223cy8ctmg110_set_sleepmode(ts, false);224225err = gpio_request(ts->irq_pin, "touch_irq_key");226if (err < 0) {227dev_err(&client->dev,228"Failed to request GPIO %d, error %d\n",229ts->irq_pin, err);230goto err_shutoff_device;231}232233err = gpio_direction_input(ts->irq_pin);234if (err < 0) {235dev_err(&client->dev,236"Failed to configure input direction for GPIO %d, error %d\n",237ts->irq_pin, err);238goto err_free_irq_gpio;239}240241client->irq = gpio_to_irq(ts->irq_pin);242if (client->irq < 0) {243err = client->irq;244dev_err(&client->dev,245"Unable to get irq number for GPIO %d, error %d\n",246ts->irq_pin, err);247goto err_free_irq_gpio;248}249250err = request_threaded_irq(client->irq, NULL, cy8ctmg110_irq_thread,251IRQF_TRIGGER_RISING, "touch_reset_key", ts);252if (err < 0) {253dev_err(&client->dev,254"irq %d busy? error %d\n", client->irq, err);255goto err_free_irq_gpio;256}257258err = input_register_device(input_dev);259if (err)260goto err_free_irq;261262i2c_set_clientdata(client, ts);263device_init_wakeup(&client->dev, 1);264return 0;265266err_free_irq:267free_irq(client->irq, ts);268err_free_irq_gpio:269gpio_free(ts->irq_pin);270err_shutoff_device:271cy8ctmg110_set_sleepmode(ts, true);272cy8ctmg110_power(ts, false);273if (ts->reset_pin)274gpio_free(ts->reset_pin);275err_free_mem:276input_free_device(input_dev);277kfree(ts);278return err;279}280281#ifdef CONFIG_PM282static int cy8ctmg110_suspend(struct device *dev)283{284struct i2c_client *client = to_i2c_client(dev);285struct cy8ctmg110 *ts = i2c_get_clientdata(client);286287if (device_may_wakeup(&client->dev))288enable_irq_wake(client->irq);289else {290cy8ctmg110_set_sleepmode(ts, true);291cy8ctmg110_power(ts, false);292}293return 0;294}295296static int cy8ctmg110_resume(struct device *dev)297{298struct i2c_client *client = to_i2c_client(dev);299struct cy8ctmg110 *ts = i2c_get_clientdata(client);300301if (device_may_wakeup(&client->dev))302disable_irq_wake(client->irq);303else {304cy8ctmg110_power(ts, true);305cy8ctmg110_set_sleepmode(ts, false);306}307return 0;308}309310static SIMPLE_DEV_PM_OPS(cy8ctmg110_pm, cy8ctmg110_suspend, cy8ctmg110_resume);311#endif312313static int __devexit cy8ctmg110_remove(struct i2c_client *client)314{315struct cy8ctmg110 *ts = i2c_get_clientdata(client);316317cy8ctmg110_set_sleepmode(ts, true);318cy8ctmg110_power(ts, false);319320free_irq(client->irq, ts);321input_unregister_device(ts->input);322gpio_free(ts->irq_pin);323if (ts->reset_pin)324gpio_free(ts->reset_pin);325kfree(ts);326327return 0;328}329330static struct i2c_device_id cy8ctmg110_idtable[] = {331{ CY8CTMG110_DRIVER_NAME, 1 },332{ }333};334335MODULE_DEVICE_TABLE(i2c, cy8ctmg110_idtable);336337static struct i2c_driver cy8ctmg110_driver = {338.driver = {339.owner = THIS_MODULE,340.name = CY8CTMG110_DRIVER_NAME,341#ifdef CONFIG_PM342.pm = &cy8ctmg110_pm,343#endif344},345.id_table = cy8ctmg110_idtable,346.probe = cy8ctmg110_probe,347.remove = __devexit_p(cy8ctmg110_remove),348};349350static int __init cy8ctmg110_init(void)351{352return i2c_add_driver(&cy8ctmg110_driver);353}354355static void __exit cy8ctmg110_exit(void)356{357i2c_del_driver(&cy8ctmg110_driver);358}359360module_init(cy8ctmg110_init);361module_exit(cy8ctmg110_exit);362363MODULE_AUTHOR("Samuli Konttila <[email protected]>");364MODULE_DESCRIPTION("cy8ctmg110 TouchScreen Driver");365MODULE_LICENSE("GPL v2");366367368