From patchwork Sun Sep 16 09:56:22 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandra Chin X-Patchwork-Id: 1462841 Return-Path: X-Original-To: patchwork-linux-input@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork2.kernel.org (Postfix) with ESMTP id BD28FDF28C for ; Sun, 16 Sep 2012 10:09:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752031Ab2IPKIV (ORCPT ); Sun, 16 Sep 2012 06:08:21 -0400 Received: from tw-mx2.synaptics.com.tw ([203.163.83.68]:56294 "EHLO tw-mx2.synaptics.com.tw" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751941Ab2IPKIT convert rfc822-to-8bit (ORCPT ); Sun, 16 Sep 2012 06:08:19 -0400 X-Greylist: delayed 610 seconds by postgrey-1.27 at vger.kernel.org; Sun, 16 Sep 2012 06:08:17 EDT Received: from unknown (HELO tww-owa1.synaptics-inc.local) ([172.25.57.15]) by tw-mx2-mgt.synaptics.com.tw with ESMTP; 16 Sep 2012 17:58:05 +0800 Received: from HKDCW-OWA1.synaptics-inc.local (10.22.10.15) by tww-owa1.synaptics-inc.local (172.25.57.15) with Microsoft SMTP Server (TLS) id 8.2.176.0; Sun, 16 Sep 2012 17:56:35 +0800 Received: from HKDCW-MAIL1.synaptics-inc.local ([fe80::81aa:2d28:dafb:74b2]) by hkdcw-owa1.synaptics-inc.local ([::1]) with mapi id 14.01.0289.001; Sun, 16 Sep 2012 17:58:55 +0800 From: Alexandra Chin To: Dmitry Torokhov CC: Linux Kernel , Linux Input , Linus Walleij , Naveen Kumar Gaddipati , Mahesh Srinivasan , Alex Chang , Scott Lin Subject: [PATCH] Input: Add new driver into Input Subsystem for Synaptics DS4 touchscreen I2C devices Thread-Topic: [PATCH] Input: Add new driver into Input Subsystem for Synaptics DS4 touchscreen I2C devices Thread-Index: AQHNk/HhVA088HpSAk6VMBpKXya6/Q== Date: Sun, 16 Sep 2012 09:56:22 +0000 Message-ID: Accept-Language: zh-TW, en-US Content-Language: zh-TW X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [10.22.10.5] MIME-Version: 1.0 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org Synaptics DS4 touchscreen driver implements a generic driver supporting I2C protocol for Synaptics Design Studio 4 (DS4) family of Touchscreen Controllers which include the following: - S32xX series - S730X series - S22xx series The driver supports multifinger pointing functionality and power management. The driver is based on the original work submitted by Linus Walleij and Naveen Kumar Gaddipati . This patch is against the v3.1-rc9 tag of Dmitry Torokhov's kernel tree, commit bd68dfe0071b50bc69416a92ee22b63d1cc33a3b. Changes in this patch: - modified: drivers/input/touchscreen/Kconfig - modified: drivers/input/touchscreen/Makefile - new file: drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c - new file: drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h - new file: include/linux/input/synaptics_dsx.h This patch is functionally tested on omap beagleboard-xm. We will continue to maintain and support this driver officially, including making updates, as well as supporting future Touch Controller revisions from Synaptics. Any comments are much appreciated. Alexandra Chin Signed-off-by: Alexandra Chin --- drivers/input/touchscreen/Kconfig | 12 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c | 1085 ++++++++++++++++++++ drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h | 94 ++ include/linux/input/synaptics_dsx.h | 49 + 5 files changed, 1241 insertions(+), 0 deletions(-) ----- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 1ba232c..431c72b 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -900,4 +900,16 @@ config TOUCHSCREEN_TPS6507X To compile this driver as a module, choose M here: the module will be called tps6507x_ts. +config TOUCHSCREEN_SYNAPTICS_DS4_RMI4_I2C + tristate "Synaptics ds4 i2c touchscreen" + depends on I2C + help + Say Y here if you have a Synaptics DS4 I2C touchscreen + connected to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called synaptics_ds4_rmi4_i2c. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 178eb12..61f5f22 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -73,3 +73,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o +obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DS4_RMI4_I2C) += synaptics_ds4_rmi4_i2c.o diff --git a/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c b/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c new file mode 100644 index 0000000..c3bf46f --- /dev/null +++ b/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c @@ -0,0 +1,1085 @@ +/* + * Synaptics DS4 touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * Copyright (C) 2010 Js HA + * Copyright (C) 2010 Naveen Kumar G + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_ds4_rmi4_i2c.h" + +#define DRIVER_NAME "synaptics_ds4_i2c" +#define INPUT_PHYS_NAME "synaptics_ds4_i2c/input0" + +#define PAGES_TO_SERVICE 0xFF +#define MAX_INTR_REGISTERS 4 +#define MAX_TOUCH_MAJOR 15 +#define BUF_LEN 37 +#define PAGE_LEN 2 +#define DATA_LEN 12 +#define QUERY_LEN 9 +#define DATA_BUF_LEN 10 +#define STD_QUERY_LEN 21 + +#define MASK_16BIT 0xFFFF +#define MASK_8BIT 0xFF +#define MASK_7BIT 0x7F +#define MASK_5BIT 0x1F +#define MASK_4BIT 0x0F +#define MASK_3BIT 0x07 +#define MASK_2BIT 0x03 +#define TOUCH_CTRL_INTR 0x8 + +#define NO_SLEEP_ON (1 << 3) +#define NO_SLEEP_OFF (0 << 3) +#define NORMAL_OPERATION (0 << 0) +#define SENSOR_SLEEP (1 << 0) +#define RMI4_NUMBER_OF_MAX_FINGERS (10) + +#ifdef CONFIG_PM +static int synaptics_rmi4_suspend(struct device *dev); +static int synaptics_rmi4_resume(struct device *dev); +#endif + +/* + * Called by synaptics_rmi4_i2c_read() and synaptics_rmi4_i2c_write(). + * + * This function writes to the page select register to switch to the + * assigned page. + */ +static int synaptics_rmi4_set_page(struct synaptics_ds4_rmi4_data *pdata, + unsigned int address) +{ + unsigned char txbuf[PAGE_LEN]; + int retval = 0; + unsigned int page; + struct i2c_client *i2c = pdata->i2c_client; + + page = ((address >> 8) & MASK_8BIT); + if (page != pdata->current_page) { + txbuf[0] = MASK_8BIT; + txbuf[1] = page; + retval = i2c_master_send(i2c, txbuf, PAGE_LEN); + if (retval != PAGE_LEN) + retval = -EIO; + else + pdata->current_page = page; + } else + retval = PAGE_LEN; + + return retval; +} + +/* + * Called by various functions in this driver + * + * This function reads data of an arbitrary length from the sensor, starting + * from an assigned register address of the sensor, via I2C with a retry + * mechanism. + */ +static int synaptics_rmi4_i2c_read(struct synaptics_ds4_rmi4_data *pdata, + unsigned short addr, unsigned char *data, + unsigned short length) +{ + int retval = 0; + unsigned char buf; + struct i2c_msg msg[] = { + { + .addr = pdata->i2c_client->addr, + .flags = 0, + .len = 1, + .buf = &buf, + }, + { + .addr = pdata->i2c_client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = data, + } + }; + buf = addr & MASK_8BIT; + + mutex_lock(&(pdata->io_ctrl_mutex)); + retval = synaptics_rmi4_set_page(pdata, addr); + if (retval != PAGE_LEN) + goto exit; + retval = i2c_transfer(pdata->i2c_client->adapter, msg, ARRAY_SIZE(msg)); + if (retval != ARRAY_SIZE(msg)) + retval = -EIO; +exit: + mutex_unlock(&(pdata->io_ctrl_mutex)); + return retval; +} + +/* + * Called by various functions in this driver + * + * This function writes data of an arbitrary length to the sensor, starting + * from an assigned register address of the sensor, via I2C with a retry + * mechanism. + */ +static int synaptics_rmi4_i2c_write(struct synaptics_ds4_rmi4_data *pdata, + unsigned short addr, unsigned char *data, + unsigned short length) +{ + int retval = 0; + unsigned char buf[length + 1]; + + struct i2c_msg msg[] = { + { + .addr = pdata->i2c_client->addr, + .flags = 0, + .len = length + 1, + .buf = buf, + } + }; + + mutex_lock(&(pdata->io_ctrl_mutex)); + retval = synaptics_rmi4_set_page(pdata, addr); + if (retval != PAGE_LEN) + goto exit; + + buf[0] = addr & MASK_8BIT; + memcpy(&buf[1], &data[0], length); + + retval = i2c_transfer(pdata->i2c_client->adapter, msg, ARRAY_SIZE(msg)); + if (retval != ARRAY_SIZE(msg)) + retval = -EIO; +exit: + mutex_unlock(&(pdata->io_ctrl_mutex)); + return 0; +} + +/* + * Called by synaptics_rmi4_report_device() when valid Function $11 + * finger data has been detected. + * + * This function reads the Function $11 data registers, determines the + * status of each finger supported by the Function, processes any + * necessary coordinate manipulation, reports the finger data to + * the input subsystem + */ +static int synaptics_rmi4_abs_report(struct synaptics_ds4_rmi4_data *pdata, + struct synaptics_ds4_rmi4_fn *function_handler) +{ + int touch_count = 0; /* number of touch points */ + int finger; + int fingers_supported; + int finger_registers; + int reg; + int finger_shift; + int finger_status; + int retval; + unsigned short data_base_addr; + unsigned short data_offset; + unsigned char data_reg_blk_size; + unsigned char values[2]; + unsigned char data[DATA_LEN]; + int x[RMI4_NUMBER_OF_MAX_FINGERS]; + int y[RMI4_NUMBER_OF_MAX_FINGERS]; + int wx[RMI4_NUMBER_OF_MAX_FINGERS]; + int wy[RMI4_NUMBER_OF_MAX_FINGERS]; + + /* get 2D sensor finger data */ + /* + * First get the finger status field - the size of the finger status + * field is determined by the number of finger supporte - 2 bits per + * finger, so the number of registers to read is: + * registerCount = ceil(numberOfFingers/4). + * Read the required number of registers and check each 2 bit field to + * determine if a finger is down: + * 00 = finger not present, + * 01 = finger present and data accurate, + * 10 = finger present but data may not be accurate, + * 11 = reserved for product use. + */ + fingers_supported = function_handler->num_of_data_points; + finger_registers = (fingers_supported + 3) / 4; + data_base_addr = function_handler->fn_full_addr.data_base_addr; + + retval = synaptics_rmi4_i2c_read(pdata, data_base_addr, values, + finger_registers); + if (retval < 0) + return retval; + /* + * For each finger present, read the proper number of registers + * to get absolute data. + */ + data_reg_blk_size = function_handler->size_of_data_register_block; + for (finger = 0; finger < fingers_supported; finger++) { + /* Determine which data byte the finger status is in */ + reg = finger / 4; + /* Bit shift to get finger's status */ + finger_shift = (finger % 4) * 2; + finger_status = (values[reg] >> finger_shift) & 3; + /* + * If finger status indicates a finger is present then + * read the finger data and report it + */ + if (finger_status == 1 || finger_status == 2) { + /* Read the finger data */ + data_offset = data_base_addr + + ((finger * data_reg_blk_size) + + finger_registers); + retval = synaptics_rmi4_i2c_read(pdata, + data_offset, data, + data_reg_blk_size); + if (retval < 0) + return retval; + else { + x[touch_count] = + (data[0] << 4) | (data[2] & MASK_4BIT); + y[touch_count] = + (data[1] << 4) | + ((data[2] >> 4) & MASK_4BIT); + wy[touch_count] = (data[3] >> 4) & MASK_4BIT; + wx[touch_count] = (data[3] & MASK_4BIT); + + if (pdata->board->x_flip) + x[touch_count] = + pdata->sensor_max_x - + x[touch_count]; + if (pdata->board->y_flip) + y[touch_count] = + pdata->sensor_max_y - + y[touch_count]; + } + /* Number of active touch points */ + touch_count++; + } + } + + /* Report to input subsystem */ + if (touch_count) { + for (finger = 0; finger < touch_count; finger++) { + input_report_abs(pdata->input_dev, ABS_MT_TOUCH_MAJOR, + max(wx[finger] , wy[finger])); + input_report_abs(pdata->input_dev, ABS_MT_POSITION_X, + x[finger]); + input_report_abs(pdata->input_dev, ABS_MT_POSITION_Y, + y[finger]); + input_mt_sync(pdata->input_dev); + } + } else + input_mt_sync(pdata->input_dev); + + /* sync after groups of events */ + input_sync(pdata->input_dev); + return touch_count; +} + +/* + * Called by synaptics_rmi4_sensor_report(). + * + * This function calls the appropriate finger data reporting function + * based on the function handler it receives + */ +static int synaptics_rmi4_report_device(struct synaptics_ds4_rmi4_data *pdata, + struct synaptics_ds4_rmi4_fn *function_handler) +{ + int touch_count = 0; + struct i2c_client *client = pdata->i2c_client; + + dev_dbg(&pdata->i2c_client->dev, "%s: function number 0x%X\n", + __func__, function_handler->fn_number); + + switch (function_handler->fn_number) { + case SYNAPTICS_DS4_RMI4_2D_SENSOR_FUNC: + touch_count = synaptics_rmi4_abs_report( + pdata, function_handler); + break; + default: + dev_info(&client->dev, "%s: F%02X not supported\n", + __func__, function_handler->fn_number); + break; + } + return touch_count; +} + +/* + * Called by synaptics_rmi4_irq(). + * + * This function determines the interrupt source(s) from the sensor and + * calls synaptics_rmi4_report_device() with the appropriate function + * handler for each function with valid data inputs. + */ +static int synaptics_rmi4_sensor_report(struct synaptics_ds4_rmi4_data *pdata) +{ + unsigned char intr_status[MAX_INTR_REGISTERS]; + int retval; + int touch_count = 0; + struct synaptics_ds4_rmi4_fn *function_handler; + struct synaptics_ds4_rmi4_device_info *rmi; + + /* + * Get the interrupt status from the function $01 + * control register+1 to find which source(s) were interrupting + * so we can read the data from the source(s) (2D sensor, buttons..) + */ + retval = synaptics_rmi4_i2c_read(pdata, + pdata->fn01_data_base_addr + 1, + intr_status, + pdata->number_of_interrupt_register); + if (retval < 0) + return 0; + + if (pdata->touch_stopped) { + dev_warn(&pdata->i2c_client->dev, + "Not ready to handle interrupts yet!\n"); + return 0; + } + /* + * Check each function that has data sources and if the interrupt for + * that triggered then call that RMI4 functions report() function to + * gather data and report it to the input subsystem + */ + rmi = &(pdata->rmi4_mod_info); + list_for_each_entry(function_handler, &rmi->support_fn_list, link) { + if (function_handler->num_of_data_sources) { + if (intr_status[function_handler->index_to_intr_reg] & + function_handler->intr_mask) + touch_count = synaptics_rmi4_report_device( + pdata, function_handler); + } + } + return touch_count; +} + +/* + * Called by the kernel when an interrupt occurs (when the sensor asserts + * the attention irq. + * + * This function is the ISR thread and handles the acquisition and + * the reporting of finger data when the presence of fingers is detected. + */ +static irqreturn_t synaptics_rmi4_irq(int irq, void *data) +{ + struct synaptics_ds4_rmi4_data *pdata = data; + int touch_count; + do { + touch_count = synaptics_rmi4_sensor_report(pdata); + if (touch_count) + wait_event_timeout(pdata->wait, pdata->touch_stopped, + msecs_to_jiffies(1)); + else + break; + } while (!pdata->touch_stopped); + return IRQ_HANDLED; +} + +/* + * Called by synaptics_rmi4_probe() and the power management functions in + * this driver + * + * This function handles the enabling of the attention irq + * including the setting up of the ISR thread. + */ +static int synaptics_rmi4_irq_enable(struct synaptics_ds4_rmi4_data *pdata, + bool request) +{ + int retval = 0; + char intr_status; + const struct synaptics_dsx_platform_data *platformdata = + pdata->i2c_client->dev.platform_data; + + mutex_lock(&pdata->irq_request_mutex); + if (pdata->irq_enabled) + goto exit; + + /* Clear interrupts */ + retval = synaptics_rmi4_i2c_read(pdata, + pdata->fn01_data_base_addr + 1, + &intr_status, + pdata->number_of_interrupt_register); + if (retval < 0) + return retval; + if (request) { + retval = request_threaded_irq(pdata->irq, NULL, + synaptics_rmi4_irq, + platformdata->irq_type, + DRIVER_NAME, pdata); + if (retval) { + dev_err(&pdata->i2c_client->dev, + "%s:Unable to get attn irq %d, type %d\n", + __func__, + pdata->irq, + platformdata->irq_type); + goto exit; + } + } else + enable_irq(pdata->irq); + pdata->irq_enabled = true; + +exit: + mutex_unlock(&pdata->irq_request_mutex); + return retval; +} + +/* + * Called by synaptics_rmi4_probe() and the power management functions in + * this driver + * + * This function handles the disabling of the attention irq + * including the setting up of the ISR thread. + */ +static int synaptics_rmi4_irq_disable(struct synaptics_ds4_rmi4_data *pdata, + bool release) +{ + int retval = 0; + mutex_lock(&pdata->irq_request_mutex); + + if (pdata->irq_enabled) { + disable_irq(pdata->irq); + if (release) + free_irq(pdata->irq, pdata); + pdata->irq_enabled = false; + } else + dev_warn(&pdata->i2c_client->dev, + "%s:irq has not been reqested\n", __func__); + mutex_unlock(&pdata->irq_request_mutex); + return retval; +} + +/* + * Called by synaptics_rmi4_query_device(). + * + * This funtion parses information from the Function 11 registers and + * determines the number of fingers supported, x and y data ranges, + * offset to the associated interrupt status register, interrupt bit mask, + * and gathers finger data acquisition capabilities from the query registers. + */ +static int synaptics_rmi4_2d_touch_detect( + struct synaptics_ds4_rmi4_data *pdata, + struct synaptics_ds4_rmi4_fn *function_handler, + struct synaptics_ds4_rmi4_fn_desc *fd, + unsigned int interruptcount) +{ + unsigned char queries[QUERY_LEN]; + unsigned char data[BUF_LEN]; + unsigned char abs_data_size; + unsigned char abs_data_blk_size; + unsigned short intr_offset; + int i; + int retval; + + function_handler->fn_number = fd->fn_number; + function_handler->num_of_data_sources = fd->intr_src_count; + + /* + * need to get number of fingers supported, data size, etc. + * to be used when getting data since the number of registers to + * read depends on the number of fingers supported and data size. + */ + retval = synaptics_rmi4_i2c_read(pdata, + function_handler->fn_full_addr.query_base_addr, + queries, sizeof(queries)); + if (retval < 0) + return retval; + + /* Number of fingers */ + if ((queries[1] & MASK_3BIT) <= 4) /* add 1 since zero based */ + function_handler->num_of_data_points = + (queries[1] & MASK_3BIT) + 1; + else + if ((queries[1] & MASK_3BIT) == 5) + function_handler->num_of_data_points = 10; + + /* Max x/y */ + retval = synaptics_rmi4_i2c_read(pdata, + function_handler->fn_full_addr.ctrl_base_addr, + data, DATA_BUF_LEN); + if (retval < 0) + return retval; + + /* Store these for use later*/ + pdata->sensor_max_x = ((data[6] & MASK_8BIT) << 0) | + ((data[7] & MASK_4BIT) << 8); + pdata->sensor_max_y = ((data[8] & MASK_8BIT) << 0) | + ((data[9] & MASK_4BIT) << 8); + + + /* Interrupt info for handling interrupts */ + function_handler->index_to_intr_reg = (interruptcount + 7) / 8; + if (function_handler->index_to_intr_reg != 0) + function_handler->index_to_intr_reg -= 1; + /* + * Loop through interrupts for each source in fn $11 + * and or in a bit to the interrupt mask for each. + */ + intr_offset = interruptcount % 8; + function_handler->intr_mask = 0; + for (i = intr_offset; + i < ((fd->intr_src_count & MASK_3BIT) + intr_offset); i++) + function_handler->intr_mask |= 1 << i; + + /* Size of just the absolute data for one finger */ + abs_data_size = queries[5] & MASK_2BIT; + + /* One each for X and Y, one for LSB for X & Y, one for W, one for Z */ + abs_data_blk_size = 3 + (2 * (abs_data_size == 0 ? 1 : 0)); + function_handler->size_of_data_register_block = abs_data_blk_size; + + return retval; +} + +static int synaptics_rmi4_alloc_func_handler( + struct synaptics_ds4_rmi4_fn **function_handler, + struct synaptics_ds4_rmi4_fn_desc *rmi_fd, + int page_number) +{ + *function_handler = + kmalloc(sizeof(struct synaptics_ds4_rmi4_fn), GFP_KERNEL); + if (!(*function_handler)) { + return -ENOMEM; + } + (*function_handler)->fn_full_addr.data_base_addr = + (rmi_fd->data_base_addr | (page_number << 8)); + (*function_handler)->fn_full_addr.ctrl_base_addr = + (rmi_fd->ctrl_base_addr | (page_number << 8)); + (*function_handler)->fn_full_addr.cmd_base_addr = + (rmi_fd->cmd_base_addr | (page_number << 8)); + (*function_handler)->fn_full_addr.query_base_addr = + (rmi_fd->query_base_addr | (page_number << 8)); + return 0; +} + +/* + * Called by synaptics_rmi4_probe(). + * + * This funtion scans the page description table, records the offsets to the + * register types of Function $01, sets up the function handlers for Function + * $11, determines the number of interrupt sources from the + * sensor, adds valid Functions with data inputs to the Function linked list, + * parses information from the query registers of Function $01, and enables + * the interrupt sources from the valid Functions with data inputs. + */ +static int synaptics_rmi4_query_device(struct synaptics_ds4_rmi4_data *pdata) +{ + int i, page_number; + int retval; + int data_sources = 0; + unsigned char std_queries[STD_QUERY_LEN]; + unsigned char interrupt_mask[MAX_INTR_REGISTERS]; + unsigned char intr_count = 0; + unsigned char intr_index = 0; + unsigned short ctrl_offset; + struct synaptics_ds4_rmi4_fn *function_handler; + struct synaptics_ds4_rmi4_fn_desc rmi_fd; + struct synaptics_ds4_rmi4_device_info *rmi; + struct i2c_client *client = pdata->i2c_client; + + /* Init the physical drivers RMI module info list of functions */ + INIT_LIST_HEAD(&pdata->rmi4_mod_info.support_fn_list); + + /* Scan the Page Descriptor Table */ + for (page_number = 0; page_number < PAGES_TO_SERVICE; page_number++) { + for (i = SYNAPTICS_DS4_RMI4_PDT_START; + i > SYNAPTICS_DS4_RMI4_PDT_END; + i -= SYNAPTICS_DS4_RMI4_PDT_ENTRY_SIZE) { + function_handler = NULL; + i |= (page_number << 8); + + retval = synaptics_rmi4_i2c_read(pdata, i, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + if (rmi_fd.fn_number == 0) /* end of the PDT */ + break; + dev_dbg(&pdata->i2c_client->dev, + "%s: function F%02X is present\n", + __func__, rmi_fd.fn_number); + switch (rmi_fd.fn_number & MASK_8BIT) { + case SYNAPTICS_DS4_RMI4_DEVICE_CONTROL_FUNC: + pdata->fn01_query_base_addr = + rmi_fd.query_base_addr; + pdata->fn01_ctrl_base_addr = + rmi_fd.ctrl_base_addr; + pdata->fn01_data_base_addr = + rmi_fd.data_base_addr; + break; + case SYNAPTICS_DS4_RMI4_2D_SENSOR_FUNC: + if (rmi_fd.intr_src_count) { + retval = + synaptics_rmi4_alloc_func_handler( + &function_handler, + &rmi_fd, + page_number); + if (retval < 0) + return retval; + retval = + synaptics_rmi4_2d_touch_detect( + pdata, + function_handler, + &rmi_fd, + intr_count); + if (retval < 0) + return retval; + } + break; + } + /* interrupt count for next iteration */ + intr_count += (rmi_fd.intr_src_count & MASK_3BIT); + /* + * add functions to the list that have data associated + */ + if (function_handler && rmi_fd.intr_src_count) { + /* link this function info to RMI module */ + mutex_lock(&(pdata->fn_list_mutex)); + dev_dbg(&pdata->i2c_client->dev, + "%s: add fiunction handler 0x%X to list\n", + __func__, function_handler->fn_number); + list_add_tail(&function_handler->link, + &pdata->rmi4_mod_info.support_fn_list); + mutex_unlock(&(pdata->fn_list_mutex)); + } + } + } + /* + * calculate the interrupt register count - used in the + * ISR to read the correct number of interrupt registers + */ + pdata->number_of_interrupt_register = (intr_count + 7) / 8; + dev_dbg(&client->dev, "%s: interrupt register count :%d\n", + __func__, + pdata->number_of_interrupt_register); + + /* Load up the standard queries and get the RMI4 module info */ + retval = synaptics_rmi4_i2c_read(pdata, pdata->fn01_query_base_addr, + std_queries, sizeof(std_queries)); + if (retval < 0) + return retval; + + /* + * get manufacturer id, product_props, product info, + * date code, tester id, serial num and product id (name) + */ + pdata->rmi4_mod_info.manufacturer_id = std_queries[0]; + pdata->rmi4_mod_info.product_props = std_queries[1]; + pdata->rmi4_mod_info.product_info[0] = std_queries[2]; + pdata->rmi4_mod_info.product_info[1] = std_queries[3]; + /* year - 2001-2032 */ + pdata->rmi4_mod_info.date_code[0] = std_queries[4] & MASK_5BIT; + /* month - 1-12 */ + pdata->rmi4_mod_info.date_code[1] = std_queries[5] & MASK_4BIT; + /* day - 1-31 */ + pdata->rmi4_mod_info.date_code[2] = std_queries[6] & MASK_5BIT; + pdata->rmi4_mod_info.tester_id = ((std_queries[7] & MASK_7BIT) << 8) | + (std_queries[8] & MASK_7BIT); + pdata->rmi4_mod_info.serial_number = + ((std_queries[9] & MASK_7BIT) << 8) | + (std_queries[10] & MASK_7BIT); + memcpy(pdata->rmi4_mod_info.product_id_string, &std_queries[11], 10); + + /* Check if this is a Synaptics device - report if not. */ + if (pdata->rmi4_mod_info.manufacturer_id != 1) + dev_err(&client->dev, "%s: non-Synaptics mfg id:%d\n", + __func__, + pdata->rmi4_mod_info.manufacturer_id); + + memset(interrupt_mask, 0x00, sizeof(interrupt_mask)); + list_for_each_entry(function_handler, + &pdata->rmi4_mod_info.support_fn_list, link) + data_sources += function_handler->num_of_data_sources; + if (data_sources) { + rmi = &(pdata->rmi4_mod_info); + list_for_each_entry(function_handler, + &rmi->support_fn_list, link) { + if (function_handler->num_of_data_sources) + intr_index = + function_handler->index_to_intr_reg; + interrupt_mask[intr_index] |= + function_handler->intr_mask; + } + } + + for (i = 0; i < MAX_INTR_REGISTERS; i++) { + if (interrupt_mask[i] != 0x00) { + dev_dbg(&client->dev, + "%s: interrupt %d enable mask :0x%X\n", + __func__, i, interrupt_mask[i]); + ctrl_offset = pdata->fn01_ctrl_base_addr + 1 + i; + retval = synaptics_rmi4_i2c_write(pdata, + ctrl_offset, + &(interrupt_mask[i]), + sizeof(interrupt_mask[i])); + if (retval < 0) + return retval; + } + } + return 0; +} + +static int __devinit synaptics_rmi4_probe( + struct i2c_client *client, const struct i2c_device_id *dev_id) +{ + int retval; + struct synaptics_ds4_rmi4_data *pdata; + struct synaptics_ds4_rmi4_fn *function_handler; + const struct synaptics_dsx_platform_data *platformdata = + client->dev.platform_data; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "i2c smbus byte data not supported\n"); + return -EIO; + } + + if (!platformdata) { + dev_err(&client->dev, "%s: no platform data\n", __func__); + return -EINVAL; + } + + /* Allocate and initialize the instance data for this client */ + pdata = kzalloc(sizeof(struct synaptics_ds4_rmi4_data) * 2, + GFP_KERNEL); + if (!pdata) { + dev_err(&client->dev, "%s: no memory allocated\n", __func__); + return -ENOMEM; + } + + pdata->input_dev = input_allocate_device(); + if (pdata->input_dev == NULL) { + dev_err(&client->dev, "%s:input device alloc failed\n", + __func__); + retval = -ENOMEM; + goto err_input; + } + + if (platformdata->regulator_en) { + pdata->regulator = regulator_get(&client->dev, "vdd"); + if (IS_ERR(pdata->regulator)) { + dev_err(&client->dev, "%s:get regulator failed\n", + __func__); + retval = PTR_ERR(pdata->regulator); + goto err_regulator; + } + regulator_enable(pdata->regulator); + } + init_waitqueue_head(&pdata->wait); + pdata->i2c_client = client; + pdata->current_page = MASK_16BIT; + pdata->board = platformdata; + pdata->sensor_sleep = false; + pdata->irq_enabled = false; + pdata->touch_stopped = false; + + /* Init the mutexes for maintain the lists */ + mutex_init(&(pdata->fn_list_mutex)); + mutex_init(&(pdata->io_ctrl_mutex)); + + /* + * Register physical driver - this will call the detect function that + * will then scan the device and determine the supported + * rmi4 functions. + */ + retval = synaptics_rmi4_query_device(pdata); + if (retval) { + dev_err(&client->dev, "%s: rmi4 query device failed\n", + __func__); + goto err_query_dev; + } + + /* Store the instance data in the i2c_client */ + i2c_set_clientdata(client, pdata); + + /* Initialize the input device parameters */ + pdata->input_dev->name = DRIVER_NAME; + pdata->input_dev->phys = INPUT_PHYS_NAME; + pdata->input_dev->id.bustype = BUS_I2C; + pdata->input_dev->dev.parent = &client->dev; + input_set_drvdata(pdata->input_dev, pdata); + + /* Initialize the function handlers for rmi4 */ + set_bit(EV_SYN, pdata->input_dev->evbit); + set_bit(EV_KEY, pdata->input_dev->evbit); + set_bit(EV_ABS, pdata->input_dev->evbit); +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, pdata->input_dev->propbit); +#endif + + input_set_abs_params(pdata->input_dev, ABS_MT_POSITION_X, 0, + pdata->sensor_max_x, 0, 0); + input_set_abs_params(pdata->input_dev, ABS_MT_POSITION_Y, 0, + pdata->sensor_max_y, 0, 0); + input_set_abs_params(pdata->input_dev, ABS_MT_TOUCH_MAJOR, 0, + MAX_TOUCH_MAJOR, 0, 0); + + retval = input_register_device(pdata->input_dev); + if (retval) { + dev_err(&client->dev, "%s:input register failed\n", __func__); + goto err_query_dev; + } + + /* Gpio configuration */ + if (platformdata->gpio_config) { + retval = platformdata->gpio_config(platformdata->gpio, true); + if (retval < 0) { + dev_err(&client->dev, + "Failed to configure GPIOs, code: %d.\n", + retval); + return retval; + } + dev_info(&client->dev, "Done with GPIO configuration.\n"); + } + pdata->irq = gpio_to_irq(platformdata->gpio); + mutex_init(&(pdata->irq_request_mutex)); + pdata->touch_stopped = false; + retval = synaptics_rmi4_irq_enable(pdata, true); + if (retval) { + dev_err(&client->dev, + "%s:Unable to get attn irq %d, type %d\n, name: %s", + __func__, pdata->irq, platformdata->irq_type, + DRIVER_NAME); + goto err_request_irq; + } + + return retval; + +err_request_irq: + input_unregister_device(pdata->input_dev); +err_query_dev: + if (platformdata->regulator_en) { + regulator_disable(pdata->regulator); + regulator_put(pdata->regulator); + } + + if (!list_empty(&pdata->rmi4_mod_info.support_fn_list)) { + list_for_each_entry(function_handler, + &pdata->rmi4_mod_info.support_fn_list, link) { + if (function_handler) { + if (function_handler->data) + kfree(function_handler->data); + kfree(function_handler); + } + } + } + +err_regulator: + input_free_device(pdata->input_dev); + pdata->input_dev = NULL; +err_input: + kfree(pdata); + + return retval; +} + +static int __devexit synaptics_rmi4_remove(struct i2c_client *client) +{ + struct synaptics_ds4_rmi4_data *pdata = i2c_get_clientdata(client); + const struct synaptics_dsx_platform_data *platformdata = pdata->board; + + pdata->touch_stopped = true; + synaptics_rmi4_irq_disable(pdata, true); + + input_unregister_device(pdata->input_dev); + if (platformdata->regulator_en) { + regulator_disable(pdata->regulator); + regulator_put(pdata->regulator); + } + kfree(pdata); + return 0; +} + +/* + * Called by synaptics_rmi4_early_suspend() and synaptics_rmi4_suspend(). + * + * This function stops finger data acquisition and puts the sensor to sleep. + */ +static void synaptics_rmi4_sensor_sleep(struct synaptics_ds4_rmi4_data *pdata) +{ + int retval; + unsigned char device_ctrl; + + if (pdata->sensor_sleep == true) + return; + + pdata->touch_stopped = true; + wake_up(&pdata->wait); + + retval = synaptics_rmi4_i2c_read(pdata, + pdata->fn01_ctrl_base_addr, + &device_ctrl, + 1); + + if (retval < 0) { + dev_err(&(pdata->input_dev->dev), + "Failed to enter sleep mode. Code: %d.\n", retval); + pdata->sensor_sleep = false; + return; + } + + device_ctrl = (device_ctrl & ~MASK_3BIT); + device_ctrl = (device_ctrl | NO_SLEEP_OFF | SENSOR_SLEEP); + + retval = synaptics_rmi4_i2c_write(pdata, pdata->fn01_ctrl_base_addr, + &device_ctrl, 1); + + if (retval < 0) { + dev_err(&(pdata->input_dev->dev), + "Failed to enter sleep mode. Code: %d.\n", retval); + pdata->sensor_sleep = false; + return; + } else + pdata->sensor_sleep = true; + + return; +} + +/* + * Called by synaptics_rmi4_resume() and synaptics_rmi4_late_resume(). + * + * This function wakes the sensor from sleep. + */ +static void synaptics_rmi4_sensor_wake(struct synaptics_ds4_rmi4_data *pdata) +{ + int retval; + unsigned char device_ctrl; + + if (pdata->sensor_sleep == false) + return; + retval = synaptics_rmi4_i2c_read(pdata, + pdata->fn01_ctrl_base_addr, + &device_ctrl, + 1); + + if (retval < 0) { + dev_err(&(pdata->input_dev->dev), + "Failed to wake from sleep mode. Code: %d.\n", + retval); + pdata->sensor_sleep = true; + return; + } + + device_ctrl = (device_ctrl & ~MASK_3BIT); + device_ctrl = (device_ctrl | NO_SLEEP_OFF | NORMAL_OPERATION); + + retval = synaptics_rmi4_i2c_write(pdata, pdata->fn01_ctrl_base_addr, + &device_ctrl, 1); + + if (retval < 0) { + dev_err(&(pdata->input_dev->dev), + "Failed to wake from sleep mode. Code: %d.\n", + retval); + pdata->sensor_sleep = true; + return; + } else + pdata->sensor_sleep = false; + + return; +} + +#ifdef CONFIG_PM +/* + * Called by the kernel during the suspend phase when the system + * enters suspend. + * + * This function stops finger data acquisition and puts the sensor to + * sleep (if not already done so during the early suspend phase), + * disables the interrupt, and turns off the power to the sensor. + */ +static int synaptics_rmi4_suspend(struct device *dev) +{ + struct synaptics_ds4_rmi4_data *pdata = dev_get_drvdata(dev); + const struct synaptics_dsx_platform_data *platformdata = pdata->board; + + synaptics_rmi4_irq_disable(pdata, false); + synaptics_rmi4_sensor_sleep(pdata); + + if (platformdata->regulator_en) + regulator_disable(pdata->regulator); + return 0; +} + +/* + * Called by the kernel during the resume phase when the system + * wakes up from suspend. + * + * This function turns on the power to the sensor, wakes the sensor + * from sleep, enables the interrupt, and starts finger data + * acquisition. + */ +static int synaptics_rmi4_resume(struct device *dev) +{ + struct synaptics_ds4_rmi4_data *pdata = dev_get_drvdata(dev); + const struct synaptics_dsx_platform_data *platformdata = pdata->board; + + if (platformdata->regulator_en) + regulator_enable(pdata->regulator); + + synaptics_rmi4_sensor_wake(pdata); + synaptics_rmi4_irq_enable(pdata, false); + pdata->touch_stopped = false; + + return 0; +} + +static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = { + .suspend = synaptics_rmi4_suspend, + .resume = synaptics_rmi4_resume, +}; +#endif + +static const struct i2c_device_id synaptics_rmi4_id_table[] = { + { DRIVER_NAME, 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table); + +static struct i2c_driver synaptics_rmi4_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &synaptics_rmi4_dev_pm_ops, +#endif + }, + .probe = synaptics_rmi4_probe, + .remove = __devexit_p(synaptics_rmi4_remove), + .id_table = synaptics_rmi4_id_table, +}; + +static int __init synaptics_rmi4_init(void) +{ + return i2c_add_driver(&synaptics_rmi4_driver); +} + +static void __exit synaptics_rmi4_exit(void) +{ + i2c_del_driver(&synaptics_rmi4_driver); +} + +module_init(synaptics_rmi4_init); +module_exit(synaptics_rmi4_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("synaptics ds4 i2c touch driver"); \ No newline at end of file diff --git a/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h b/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h new file mode 100644 index 0000000..58329d9 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h @@ -0,0 +1,94 @@ +/* + * Synaptics DS4 touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * Copyright (C) 2010 Js HA + * Copyright (C) 2010 Naveen Kumar G + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _SYNAPTICS_DS4_RMI4_H_ +#define _SYNAPTICS_DS4_RMI4_H_ + +#define SYNAPTICS_DS4_RMI4_PDT_START (0x00E9) +#define SYNAPTICS_DS4_RMI4_PDT_END (0x000A) +#define SYNAPTICS_DS4_RMI4_PDT_ENTRY_SIZE (0x0006) +#define SYNAPTICS_DS4_RMI4_DEVICE_CONTROL_FUNC (0x01) +#define SYNAPTICS_DS4_RMI4_2D_SENSOR_FUNC (0x11) + +/* funtion descriptor information */ +struct synaptics_ds4_rmi4_fn_desc { + unsigned char query_base_addr; + unsigned char cmd_base_addr; + unsigned char ctrl_base_addr; + unsigned char data_base_addr; + unsigned char intr_src_count; + unsigned char fn_number; +}; + +/* funtion information */ +struct synaptics_ds4_rmi4_fn { + unsigned char fn_number; + unsigned char num_of_data_sources; + unsigned char num_of_data_points; + unsigned char size_of_data_register_block; + unsigned char index_to_intr_reg; + unsigned char intr_mask; + struct synaptics_ds4_rmi4_fn_desc fn_full_addr; + struct list_head link; + void *data; +}; + +/* device information */ +struct synaptics_ds4_rmi4_device_info { + unsigned char manufacturer_id; + unsigned char product_props; + unsigned char product_info[2]; + unsigned char date_code[3]; + unsigned short tester_id; + unsigned short serial_number; + unsigned char product_id_string[11]; + struct list_head support_fn_list; +}; + +/* ds4 rmi4 touch screen data */ +struct synaptics_ds4_rmi4_data { + const struct synaptics_dsx_platform_data *board; + struct input_dev *input_dev; + struct i2c_client *i2c_client; + struct mutex fn_list_mutex; + struct mutex io_ctrl_mutex; + struct mutex irq_request_mutex; + struct synaptics_ds4_rmi4_device_info rmi4_mod_info; + struct regulator *regulator; + unsigned int number_of_interrupt_register; + unsigned short current_page; + unsigned short fn01_ctrl_base_addr; + unsigned short fn01_query_base_addr; + unsigned short fn01_data_base_addr; + unsigned short sensor_max_x; + unsigned short sensor_max_y; + wait_queue_head_t wait; + bool touch_stopped; + bool irq_enabled; + bool sensor_sleep; + int irq; +}; + +#endif diff --git a/include/linux/input/synaptics_dsx.h b/include/linux/input/synaptics_dsx.h new file mode 100644 index 0000000..4f0d0d6 --- /dev/null +++ b/include/linux/input/synaptics_dsx.h @@ -0,0 +1,49 @@ +/* + * Synaptics DS4 touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * Copyright (C) 2010 Js HA + * Copyright (C) 2010 Naveen Kumar G + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _SYNAPTICS_DSX_H_ +#define _SYNAPTICS_DSX_H_ + +/* + * struct synaptics_dsx_platform_data - contains the dsx platform data + * @x_flip: x flip flag + * @y_flip: y flip flag + * @regulator_en: regulator enable flag + * @irq_type: irq type + * @gpio: gpio pin assignment + * @gpio_config: callback for gpio set up + * + * This structure gives platform data for dsx. + */ +struct synaptics_dsx_platform_data { + bool x_flip; + bool y_flip; + bool regulator_en; + int irq_type; + unsigned gpio; + int (*gpio_config)(unsigned interrupt_gpio, bool configure); +}; + +#endif