From patchwork Fri Jul 16 13:10:16 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alan Cox X-Patchwork-Id: 112441 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.4/8.14.3) with ESMTP id o6GDf8xu017387 for ; Fri, 16 Jul 2010 13:41:08 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965421Ab0GPNlI (ORCPT ); Fri, 16 Jul 2010 09:41:08 -0400 Received: from mga03.intel.com ([143.182.124.21]:53924 "EHLO mga03.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S965397Ab0GPNlG (ORCPT ); Fri, 16 Jul 2010 09:41:06 -0400 Received: from azsmga001.ch.intel.com ([10.2.17.19]) by azsmga101.ch.intel.com with ESMTP; 16 Jul 2010 06:40:56 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.55,214,1278313200"; d="scan'208";a="300933152" Received: from unknown (HELO localhost.localdomain) ([10.255.13.136]) by azsmga001.ch.intel.com with ESMTP; 16 Jul 2010 06:40:52 -0700 From: Alan Cox Subject: [PATCH] Synaptics TM1217 Touchscreen Controller driver To: linux-input@vger.kernel.org, dmitry.torokhov@gmail.com Date: Fri, 16 Jul 2010 14:10:16 +0100 Message-ID: <20100716130926.12814.59708.stgit@localhost.localdomain> User-Agent: StGIT/0.14.3 MIME-Version: 1.0 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Fri, 16 Jul 2010 13:41:09 +0000 (UTC) diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 8393f47..0aa3c0b 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -626,4 +626,16 @@ config TOUCHSCREEN_CY8CTMG110 To compile this driver as a module, choose M here: the module will be called cy8ctmg110_ts. +config TOUCHSCREEN_CLEARPAD_TM1217 + tristate "Synaptics Clearpad TM1217" + depends on I2C + depends on GPIOLIB + help + Say Y here if you have a Synaptics Clearpad TM1217 Controller + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called clearpad_tm1217. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 662d066..fd6720a 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o +obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217) += clearpad_tm1217.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o diff --git a/drivers/input/touchscreen/clearpad_tm1217.c b/drivers/input/touchscreen/clearpad_tm1217.c new file mode 100644 index 0000000..de3e62c --- /dev/null +++ b/drivers/input/touchscreen/clearpad_tm1217.c @@ -0,0 +1,674 @@ +/* + * clearpad_tm1217.c - Touch Screen driver for Synaptics Clearpad + * TM1217 controller + * + * Copyright (C) 2008 Intel Corp + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; ifnot, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * Questions/Comments/Bug fixes to Ramesh Agarwal (ramesh.agarwal@intel.com) + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CPTM1217_DEVICE_NAME "cptm1217" +#define CPTM1217_DRIVER_NAME CPTM1217_DEVICE_NAME + +#define MAX_TOUCH_SUPPORTED 2 +#define TOUCH_SUPPORTED 1 +#define SAMPLING_FREQ 80 /* Frequency in HZ */ +#define DELAY_BTWIN_SAMPLE (1000 / SAMPLING_FREQ) +#define WAIT_FOR_RESPONSE 5 /* 5msec just works */ +#define MAX_RETRIES 5 /* As above */ +#define INCREMENTAL_DELAY 5 /* As above */ + +/* Regster Definitions */ +#define TMA1217_DEV_STATUS 0x13 /* Device Status */ +#define TMA1217_INT_STATUS 0x14 /* Interrupt Status */ + +/* Controller can detect upto 2 possible finger touches. + * Each finger touch provides 12 bit X Y co-ordinates, the values are split + * across 2 registers, and an 8 bit Z value */ +#define TMA1217_FINGER_STATE 0x18 /* Finger State */ +#define TMA1217_FINGER1_X_HIGHER8 0x19 /* Higher 8 bit of X coordinate */ +#define TMA1217_FINGER1_Y_HIGHER8 0x1A /* Higher 8 bit of Y coordinate */ +#define TMA1217_FINGER1_XY_LOWER4 0x1B /* Lower 4 bits of X and Y */ +#define TMA1217_FINGER1_Z_VALUE 0x1D /* 8 bit Z value for finger 1 */ +#define TMA1217_FINGER2_X_HIGHER8 0x1E /* Higher 8 bit of X coordinate */ +#define TMA1217_FINGER2_Y_HIGHER8 0x1F /* Higher 8 bit of Y coordinate */ +#define TMA1217_FINGER2_XY_LOWER4 0x20 /* Lower 4 bits of X and Y */ +#define TMA1217_FINGER2_Z_VALUE 0x22 /* 8 bit Z value for finger 2 */ +#define TMA1217_DEVICE_CTRL 0x23 /* Device Control */ +#define TMA1217_INTERRUPT_ENABLE 0x24 /* Interrupt Enable */ +#define TMA1217_REPORT_MODE 0x2B /* Reporting Mode */ +#define TMA1217_MAX_X_LOWER8 0x31 /* Bit 0-7 for Max X */ +#define TMA1217_MAX_X_HIGHER4 0x32 /* Bit 8-11 for Max X */ +#define TMA1217_MAX_Y_LOWER8 0x33 /* Bit 0-7 for Max Y */ +#define TMA1217_MAX_Y_HIGHER4 0x34 /* Bit 8-11 for Max Y */ +#define TMA1217_DEVICE_CMD_RESET 0x67 /* Device CMD reg for reset */ +#define TMA1217_DEVICE_CMD_REZERO 0x69 /* Device CMD reg for rezero */ + +#define TMA1217_MANUFACTURER_ID 0x73 /* Manufacturer Id */ +#define TMA1217_PRODUCT_FAMILY 0x75 /* Product Family */ +#define TMA1217_FIRMWARE_REVISION 0x76 /* Firmware Revision */ +#define TMA1217_SERIAL_NO_HIGH 0x7C /* Bit 8-15 of device serial no. */ +#define TMA1217_SERIAL_NO_LOW 0x7D /* Bit 0-7 of device serial no. */ +#define TMA1217_PRODUCT_ID_START 0x7E /* Start address for 10 byte ID */ +#define TMA1217_DEVICE_CAPABILITY 0x8B /* Reporting capability */ + + +/* + * The touch position structure. + */ +struct touch_state { + int x; + int y; + bool button; +}; + +/* Device Specific info given by the controller */ +struct cp_dev_info { + u16 maxX; + u16 maxY; +}; + +/* Vendor related info given by the controller */ +struct cp_vendor_info { + u8 vendor_id; + u8 product_family; + u8 firmware_rev; + u16 serial_no; +}; + +/* + * Private structure to store the device details + */ +struct cp_tm1217_device { + struct i2c_client *client; + struct device *dev; + struct cp_vendor_info vinfo; + struct cp_dev_info dinfo; + struct input_dev_info { + char phys[32]; + char name[128]; + struct input_dev *input; + struct touch_state touch; + } cp_input_info[MAX_TOUCH_SUPPORTED]; + + int thread_running; + struct mutex thread_mutex; + + int gpio; +}; + + +/* The following functions are used to read/write registers on the device + * as per the RMI prorocol. Technically, a page select should be written + * before doing read/write but since the register offsets are below 0xFF + * we can use the default value of page which is 0x00 + */ +static int cp_tm1217_read(struct cp_tm1217_device *ts, + u8 *req, int size) +{ + int i, retval; + + /* Send the address */ + retval = i2c_master_send(ts->client, &req[0], 1); + if (retval != 1) { + dev_err(ts->dev, "cp_tm1217: I2C send failed\n"); + return retval; + } + msleep(WAIT_FOR_RESPONSE); + for (i = 0; i < MAX_RETRIES; i++) { + retval = i2c_master_recv(ts->client, &req[1], size); + if (retval == size) { + break; + } else { + msleep(INCREMENTAL_DELAY); + dev_dbg(ts->dev, "cp_tm1217: Retry count is %d\n", i); + } + } + if (retval != size) + dev_err(ts->dev, "cp_tm1217: Read from device failed\n"); + + return retval; +} + +static int cp_tm1217_write(struct cp_tm1217_device *ts, + u8 *req, int size) +{ + int retval; + + /* Send the address and the data to be written */ + retval = i2c_master_send(ts->client, &req[0], size + 1); + if (retval != size + 1) { + dev_err(ts->dev, "cp_tm1217: I2C write failed: %d\n", retval); + return retval; + } + /* Wait for the write to complete. TBD why this is required */ + msleep(WAIT_FOR_RESPONSE); + + return size; +} + +static int cp_tm1217_mask_interrupt(struct cp_tm1217_device *ts) +{ + u8 req[2]; + int retval; + + req[0] = TMA1217_INTERRUPT_ENABLE; + req[1] = 0x0; + retval = cp_tm1217_write(ts, req, 1); + if (retval != 1) + return -EIO; + + return 0; +} + +static int cp_tm1217_unmask_interrupt(struct cp_tm1217_device *ts) +{ + u8 req[2]; + int retval; + + req[0] = TMA1217_INTERRUPT_ENABLE; + req[1] = 0xa; + retval = cp_tm1217_write(ts, req, 1); + if (retval != 1) + return -EIO; + + return 0; +} + +static void process_touch(struct cp_tm1217_device *ts, int index) +{ + int retval; + struct input_dev_info *input_info = + (struct input_dev_info *)&ts->cp_input_info[index]; + u8 xy_data[6]; + + if (index == 0) + xy_data[0] = TMA1217_FINGER1_X_HIGHER8; + else + xy_data[0] = TMA1217_FINGER2_X_HIGHER8; + + retval = cp_tm1217_read(ts, xy_data, 5); + if (retval < 5) { + dev_err(ts->dev, "cp_tm1217: XY read from device failed\n"); + return; + } + + /* Note: Currently not using the Z values but may be requried in + the future. */ + input_info->touch.x = (xy_data[1] << 4) + | (xy_data[3] & 0x0F); + input_info->touch.y = (xy_data[2] << 4) + | ((xy_data[3] & 0xF0) >> 4); + input_report_abs(input_info->input, ABS_X, input_info->touch.x); + input_report_abs(input_info->input, ABS_Y, input_info->touch.y); + input_sync(input_info->input); +} + +static void cp_tm1217_get_data(struct cp_tm1217_device *ts) +{ + u8 req[2]; + int retval, i, finger_touched; + + do { + req[0] = TMA1217_FINGER_STATE; + retval = cp_tm1217_read(ts, req, 1); + if (retval != 1) { + dev_err(ts->dev, + "cp_tm1217: Read from device failed\n"); + continue; + } + finger_touched = 0; + /* Start sampling until the pressure is below + threshold */ + for (i = 0; i < TOUCH_SUPPORTED; i++) { + if (req[1] & 0x3) { + finger_touched++; + if (ts->cp_input_info[i].touch.button == 0) { + /* send the button touch event */ + input_report_key( + ts->cp_input_info[i].input, + BTN_TOUCH, 1); + ts->cp_input_info[i].touch.button = 1; + } + process_touch(ts, i); + } else { + if (ts->cp_input_info[i].touch.button == 1) { + /* send the button release event */ + input_report_key( + ts->cp_input_info[i].input, + BTN_TOUCH, 0); + ts->cp_input_info[i].touch.button = 0; + } + } + req[1] = req[1] >> 2; + } + msleep(DELAY_BTWIN_SAMPLE); + } while (finger_touched > 0); +} + +static irqreturn_t cp_tm1217_sample_thread(int irq, void *handle) +{ + struct cp_tm1217_device *ts = (struct cp_tm1217_device *) handle; + u8 req[2]; + int retval; + + /* Chedk if another thread is already running */ + mutex_lock(&ts->thread_mutex); + if (ts->thread_running == 1) { + mutex_unlock(&ts->thread_mutex); + return IRQ_HANDLED; + } else { + ts->thread_running = 1; + mutex_unlock(&ts->thread_mutex); + } + + /* Mask the interrupts */ + retval = cp_tm1217_mask_interrupt(ts); + + /* Read the Interrupt Status register to find the cause of the + Interrupt */ + req[0] = TMA1217_INT_STATUS; + retval = cp_tm1217_read(ts, req, 1); + if (retval != 1) + goto exit_thread; + + if (!(req[1] & 0x8)) + goto exit_thread; + + cp_tm1217_get_data(ts); + +exit_thread: + /* Unmask the interrupts before going to sleep */ + retval = cp_tm1217_unmask_interrupt(ts); + + mutex_lock(&ts->thread_mutex); + ts->thread_running = 0; + mutex_unlock(&ts->thread_mutex); + + return IRQ_HANDLED; +} + +static int cp_tm1217_init_data(struct cp_tm1217_device *ts) +{ + int retval; + u8 req[2]; + + /* Read the vendor id/ fw revision etc. Ignoring return check as this + is non critical info */ + req[0] = TMA1217_MANUFACTURER_ID; + retval = cp_tm1217_read(ts, req, 1); + ts->vinfo.vendor_id = req[1]; + + req[0] = TMA1217_PRODUCT_FAMILY; + retval = cp_tm1217_read(ts, req, 1); + ts->vinfo.product_family = req[1]; + + req[0] = TMA1217_FIRMWARE_REVISION; + retval = cp_tm1217_read(ts, req, 1); + ts->vinfo.firmware_rev = req[1]; + + req[0] = TMA1217_SERIAL_NO_HIGH; + retval = cp_tm1217_read(ts, req, 1); + ts->vinfo.serial_no = (req[1] << 8); + + req[0] = TMA1217_SERIAL_NO_LOW; + retval = cp_tm1217_read(ts, req, 1); + ts->vinfo.serial_no = ts->vinfo.serial_no | req[1]; + + req[0] = TMA1217_MAX_X_HIGHER4; + retval = cp_tm1217_read(ts, req, 1); + ts->dinfo.maxX = (req[1] & 0xF) << 8; + + req[0] = TMA1217_MAX_X_LOWER8; + retval = cp_tm1217_read(ts, req, 1); + ts->dinfo.maxX = ts->dinfo.maxX | req[1]; + + req[0] = TMA1217_MAX_Y_HIGHER4; + retval = cp_tm1217_read(ts, req, 1); + ts->dinfo.maxY = (req[1] & 0xF) << 8; + + req[0] = TMA1217_MAX_Y_LOWER8; + retval = cp_tm1217_read(ts, req, 1); + ts->dinfo.maxY = ts->dinfo.maxY | req[1]; + + return 0; + +} + +/* + * Set up a GPIO for use as the interrupt. We can't simply do this at + * boot time because the GPIO drivers themselves may not be around at + * boot/firmware set up time to do the work. Instead defer it to driver + * detection. + */ + +static int cp_tm1217_setup_gpio_irq(struct cp_tm1217_device *ts) +{ + int retval; + + /* Hook up the irq handler */ + retval = gpio_request(ts->gpio, "cp_tm1217_touch"); + if (retval < 0) { + dev_err(ts->dev, "cp_tm1217: GPIO request failed error %d\n", + retval); + return retval; + } + + retval = gpio_direction_input(ts->gpio); + if (retval < 0) { + dev_err(ts->dev, + "cp_tm1217: GPIO direction configuration failed, error %d\n", + retval); + gpio_free(ts->gpio); + return retval; + } + + retval = gpio_to_irq(ts->gpio); + if (retval < 0) { + dev_err(ts->dev, "cp_tm1217: GPIO to IRQ failedi," + " error %d\n", retval); + gpio_free(ts->gpio); + } + dev_dbg(ts->dev, + "cp_tm1217: Got IRQ number is %d for GPIO %d\n", + retval, ts->gpio); + return retval; +} + +static int cp_tm1217_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cp_tm1217_device *ts; + struct input_dev *input_dev; + struct input_dev_info *input_info; + struct cp_tm1217_platform_data *pdata; + u8 req[2]; + int i, retval; + + /* No pdata is fine - we then use "normal" IRQ mode */ + + pdata = client->dev.platform_data; + + ts = kzalloc(sizeof(struct cp_tm1217_device), GFP_KERNEL); + if (!ts) { + dev_err(ts->dev, + "cp_tm1217: Private Device Struct alloc failed\n"); + return -ENOMEM; + } + + ts->client = client; + ts->dev = &client->dev; + i2c_set_clientdata(client, ts); + + ts->thread_running = 0; + mutex_init(&ts->thread_mutex); + + /* Reset the Controller */ + req[0] = TMA1217_DEVICE_CMD_RESET; + req[1] = 0x1; + retval = cp_tm1217_write(ts, req, 1); + if (retval != 1) { + dev_err(ts->dev, "cp_tm1217: Controller reset failed\n"); + kfree(ts); + return -EIO; + } + + /* Clear up the interrupt status from reset. */ + req[0] = TMA1217_INT_STATUS; + retval = cp_tm1217_read(ts, req, 1); + + /* Mask all the interrupts */ + retval = cp_tm1217_mask_interrupt(ts); + + /* Read the controller information */ + cp_tm1217_init_data(ts); + + /* The following code will register multiple event devices when + multi-pointer is enabled, the code has not been tested + with MPX */ + for (i = 0; i < TOUCH_SUPPORTED; i++) { + input_dev = input_allocate_device(); + if (input_dev == NULL) { + kfree(ts); + dev_err(ts->dev, + "cp_tm1217:Input Device Struct alloc failed\n"); + return -ENOMEM; + } + input_info = &ts->cp_input_info[i]; + snprintf(input_info->name, sizeof(input_info->name), + "cp_tm1217_touchscreen_%d", i); + input_dev->name = input_info->name; + snprintf(input_info->phys, sizeof(input_info->phys), + "%s/input%d", dev_name(&client->dev), i); + + input_dev->phys = input_info->phys; + input_dev->id.bustype = BUS_I2C; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, 0, ts->dinfo.maxX, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, ts->dinfo.maxY, 0, 0); + + retval = input_register_device(input_dev); + if (retval) { + dev_err(ts->dev, + "Input dev registration failed for %s\n", + input_dev->name); + goto fail; + } + input_info->input = input_dev; + } + + /* Setup the reporting mode to send an interrupt only when + finger arrives or departs. */ + req[0] = TMA1217_REPORT_MODE; + req[1] = 0x02; + retval = cp_tm1217_write(ts, req, 1); + + /* Setup the device to no sleep mode for now and make it configured */ + req[0] = TMA1217_DEVICE_CTRL; + req[1] = 0x84; + retval = cp_tm1217_write(ts, req, 1); + + /* Check for the status of the device */ + req[0] = TMA1217_DEV_STATUS; + retval = cp_tm1217_read(ts, req, 1); + if (req[1] != 0) { + dev_err(ts->dev, + "cp_tm1217: Device Status 0x%x != 0: config failed\n", + req[1]); + + retval = -EIO; + goto fail; + } + + if (pdata && pdata->gpio) { + ts->gpio = pdata->gpio; + retval = cp_tm1217_setup_gpio_irq(ts); + } else + retval = client->irq; + + if (retval < 0) { + dev_err(ts->dev, "cp_tm1217: GPIO request failed error %d\n", + retval); + goto fail; + } + + client->irq = retval; + + + retval = request_threaded_irq(client->irq, + NULL, cp_tm1217_sample_thread, + IRQF_TRIGGER_FALLING, "cp_tm1217_touch", ts); + if (retval < 0) { + dev_err(ts->dev, "cp_tm1217: Request IRQ error %d\n", retval); + goto fail_gpio; + } + + /* Unmask the interrupts */ + retval = cp_tm1217_unmask_interrupt(ts); + if (retval == 0) + return 0; + + free_irq(client->irq, ts); +fail_gpio: + if (ts->gpio) + gpio_free(ts->gpio); +fail: + /* Clean up before returning failure */ + for (i = 0; i < TOUCH_SUPPORTED; i++) { + if (ts->cp_input_info[i].input) { + input_unregister_device(ts->cp_input_info[i].input); + input_free_device(ts->cp_input_info[i].input); + } + } + kfree(ts); + return retval; + +} + +/* + * cp_tm1217 suspend + * + */ +static int cp_tm1217_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct cp_tm1217_device *ts = i2c_get_clientdata(client); + u8 req[2]; + int retval; + + /* Put the controller to sleep */ + req[0] = TMA1217_DEVICE_CTRL; + retval = cp_tm1217_read(ts, req, 1); + req[1] = (req[1] & 0xF8) | 0x1; + retval = cp_tm1217_write(ts, req, 1); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + return 0; +} + +/* + * cp_tm1217_resume + * + */ +static int cp_tm1217_resume(struct i2c_client *client) +{ + struct cp_tm1217_device *ts = i2c_get_clientdata(client); + u8 req[2]; + int retval; + + /* Take the controller out of sleep */ + req[0] = TMA1217_DEVICE_CTRL; + retval = cp_tm1217_read(ts, req, 1); + req[1] = (req[1] & 0xF8) | 0x4; + retval = cp_tm1217_write(ts, req, 1); + + /* Restore the register settings sinc the power to the + could have been cut off */ + + /* Setup the reporting mode to send an interrupt only when + finger arrives or departs. */ + req[0] = TMA1217_REPORT_MODE; + req[1] = 0x02; + retval = cp_tm1217_write(ts, req, 1); + + /* Setup the device to no sleep mode for now and make it configured */ + req[0] = TMA1217_DEVICE_CTRL; + req[1] = 0x84; + retval = cp_tm1217_write(ts, req, 1); + + /* Setup the interrupt mask */ + retval = cp_tm1217_unmask_interrupt(ts); + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + return 0; +} + +/* + * cp_tm1217_remove + * + */ +static int cp_tm1217_remove(struct i2c_client *client) +{ + struct cp_tm1217_device *ts = i2c_get_clientdata(client); + int i; + + free_irq(client->irq, ts); + if (ts->gpio) + gpio_free(ts->gpio); + for (i = 0; i < TOUCH_SUPPORTED; i++) + input_unregister_device(ts->cp_input_info[i].input); + kfree(ts); + return 0; +} + +static struct i2c_device_id cp_tm1217_idtable[] = { + { CPTM1217_DEVICE_NAME, 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, cp_tm1217_idtable); + +static struct i2c_driver cp_tm1217_driver = { + .driver = { + .owner = THIS_MODULE, + .name = CPTM1217_DRIVER_NAME, + }, + .id_table = cp_tm1217_idtable, + .probe = cp_tm1217_probe, + .remove = cp_tm1217_remove, + .suspend = cp_tm1217_suspend, + .resume = cp_tm1217_resume, +}; + +static int __init clearpad_tm1217_init(void) +{ + return i2c_add_driver(&cp_tm1217_driver); +} + +static void __exit clearpad_tm1217_exit(void) +{ + i2c_del_driver(&cp_tm1217_driver); +} + +module_init(clearpad_tm1217_init); +module_exit(clearpad_tm1217_exit); + +MODULE_AUTHOR("Ramesh Agarwal "); +MODULE_DESCRIPTION("Synaptics TM1217 TouchScreen Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/spi/cp_tm1217.h b/include/linux/spi/cp_tm1217.h new file mode 100644 index 0000000..5ab13a8 --- /dev/null +++ b/include/linux/spi/cp_tm1217.h @@ -0,0 +1,9 @@ +#ifndef __LINUX_SPI_CP_TM1217_H +#define __LINUX_SPI_CP_TM1217_H + +struct cp_tm1217_platform_data +{ + int gpio; /* If not set uses the IRQ resource 0 */ +}; + +#endif