Synaptics TM1217 Touchscreen Controller driver
Subsystems (3)
PATCH 1/1 Synaptics TM1217 Touchscreen Controller driver
From: Ramesh Agarwal <[email protected]>
Driver for the Synaptics TM1217 touchscreen controller, connected via an SPI and GPIO interface.
Some clean up by Alan Cox
- Extract gpio support and IRQ support more sanely
- Tidying
---
drivers/input/touchscreen/Kconfig | 12 drivers/input/touchscreen/Makefile | 1 drivers/input/touchscreen/clearpad_tm1217.c | 674 +++++++++++++++++++++++++++ include/linux/spi/cp_tm1217.h | 9 4 files changed, 696 insertions(+), 0 deletions(-) create mode 100644 drivers/input/touchscreen/clearpad_tm1217.c create mode 100644 include/linux/spi/cp_tm1217.h
drivers/input/touchscreen/Kconfig +12 -0
@@ -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
drivers/input/touchscreen/Makefile +1 -0
@@ -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
drivers/input/touchscreen/clearpad_tm1217.c new file +674 -0
674 lines not on this page · raw message
include/linux/spi/cp_tm1217.h new file +9 -0
@@ -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
Dmitry Torokhov
maintainerHi Alan,
Quoted 5 lines · Alan Cox
From: Ramesh Agarwal <[email protected]>
Driver for the Synaptics TM1217 touchscreen controller, connected via an SPI and GPIO interface.
Is this RMI3 or RMI4 hardware? Synaptics has posted and generic-ish framework for RMI4 devices so if it is RMI4 we'd need to move over to it.
drivers/input/touchscreen/clearpad_tm1217.c line 122
Quoted 11 lines
drivers/input/touchscreen/clearpad_tm1217.c new file +11
-0+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 ];
This is a non-starter. If this is a multitouch device please use multitouch protocol for it, not register 2 separate input devices.
Quoted 153 lines
drivers/input/touchscreen/clearpad_tm1217.c new file +153
-0+ +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);
drivers/input/touchscreen/clearpad_tm1217.c line 285
Quoted 10 lines
drivers/input/touchscreen/clearpad_tm1217.c new file +10
-0+ }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 */
Just how exactly can it ihappen?
Quoted 115 lines
drivers/input/touchscreen/clearpad_tm1217.c new file +115
-0+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);
drivers/input/touchscreen/clearpad_tm1217.c line 410
Quoted 10 lines
drivers/input/touchscreen/clearpad_tm1217.c new file +10
-0+ } +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) +{
__devinit
Quoted 23 lines
drivers/input/touchscreen/clearpad_tm1217.c new file +23
-0+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 ;
drivers/input/touchscreen/clearpad_tm1217.c line 443
Quoted 10 lines
drivers/input/touchscreen/clearpad_tm1217.c new file +10
-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 ;
This mixes in-line and out-of line error unwinding, very confusing. Just goto into error path as you do in the rest of the function.
Quoted 44 lines
drivers/input/touchscreen/clearpad_tm1217.c new file +44
-0+ } + +/* 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);
drivers/input/touchscreen/clearpad_tm1217.c line 497
Quoted 10 lines
drivers/input/touchscreen/clearpad_tm1217.c new file +10
-0+ 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 );
Not acting upon retval?
drivers/input/touchscreen/clearpad_tm1217.c line 502
Quoted 5 lines
drivers/input/touchscreen/clearpad_tm1217.c new file +5
-0+ +/* 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 );
Same...
drivers/input/touchscreen/clearpad_tm1217.c line 506
Quoted 4 lines
drivers/input/touchscreen/clearpad_tm1217.c new file +4
-0+ +/* Check for the status of the device */ + req[0 ] =TMA1217_DEV_STATUS ; + retval =cp_tm1217_read (ts, req,1 );
Same...
Quoted 48 lines
drivers/input/touchscreen/clearpad_tm1217.c new file +48
-0+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); + }
drivers/input/touchscreen/clearpad_tm1217.c line 564
Quoted 10 lines
drivers/input/touchscreen/clearpad_tm1217.c new file +10
-0+ } +kfree (ts); +return retval; + +} + +/* +* cp_tm1217 suspend +* +*/
#ifdef CONFIG_PM
Quoted 52 lines
drivers/input/touchscreen/clearpad_tm1217.c new file +52
-0+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))
drivers/input/touchscreen/clearpad_tm1217.c line 626
Quoted 10 lines
drivers/input/touchscreen/clearpad_tm1217.c new file +10
-0+disable_irq_wake (client->irq); + +return 0 ; +} + +/* +* cp_tm1217_remove +* +*/ +static int cp_tm1217_remove (struct i2c_client *client)
__devexit
Quoted 18 lines
drivers/input/touchscreen/clearpad_tm1217.c new file +18
-0+{ +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 }, + { } +}; +
drivers/input/touchscreen/clearpad_tm1217.c line 654
Quoted 10 lines
drivers/input/touchscreen/clearpad_tm1217.c new file +10
-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,
__devexit_p
Quoted 36 lines
drivers/input/touchscreen/clearpad_tm1217.c new file +20
-0+ .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 <[email protected]>" ); +MODULE_DESCRIPTION ("Synaptics TM1217 TouchScreen Driver" ); +MODULE_LICENSE ("GPL v2" );include/linux/spi/cp_tm1217.h new file +9
-0@@ -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
Signature · 1 line
Dmitry
Quoted 3 lines
Is this RMI3 or RMI4 hardware? Synaptics has posted and generic-ish framework for RMI4 devices so if it is RMI4 we'd need to move over to it.
RIM4 - when will the Synaptics patches get merged ?
Moving over to use their framework providing its going to end up in tree soonish looks sensible enough.
Quoted 5 lines · Alan Cox
Quoted 3 lines
Is this RMI3 or RMI4 hardware? Synaptics has posted and generic-ish framework for RMI4 devices so if it is RMI4 we'd need to move over to it.
RIM4 - when will the Synaptics patches get merged ?
We'll be submitting an updated patch in the next 24 hours.
Quoted 2 lines
Moving over to use their framework providing its going to end up in tree soonish looks sensible enough.