|
@@ -0,0 +1,1179 @@
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * Synaptics Register Mapped Interface (RMI4) I2C Physical Layer Driver.
|
|
|
+ * Copyright (c) 2007-2010, Synaptics Incorporated
|
|
|
+ *
|
|
|
+ * Author: Js HA <js.ha@stericsson.com> for ST-Ericsson
|
|
|
+ * Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
|
|
|
+ * Copyright 2010 (c) ST-Ericsson AB
|
|
|
+ */
|
|
|
+/*
|
|
|
+ * This file is licensed under the GPL2 license.
|
|
|
+ *
|
|
|
+ *#############################################################################
|
|
|
+ * GPL
|
|
|
+ *
|
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
|
+ * under the terms of the GNU General Public License version 2 as published
|
|
|
+ * by the Free Software Foundation.
|
|
|
+ *
|
|
|
+ * 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.
|
|
|
+ *
|
|
|
+ *#############################################################################
|
|
|
+ */
|
|
|
+
|
|
|
+#include <linux/input.h>
|
|
|
+#include <linux/slab.h>
|
|
|
+#include <linux/i2c.h>
|
|
|
+#include <linux/interrupt.h>
|
|
|
+#include <linux/regulator/consumer.h>
|
|
|
+#include "synaptics_i2c_rmi4.h"
|
|
|
+
|
|
|
+/* TODO: for multiple device support will need a per-device mutex */
|
|
|
+#define DRIVER_NAME "synaptics_rmi4_i2c"
|
|
|
+
|
|
|
+#define MAX_ERROR_REPORT 6
|
|
|
+#define MAX_TOUCH_MAJOR 15
|
|
|
+#define MAX_RETRY_COUNT 5
|
|
|
+#define STD_QUERY_LEN 21
|
|
|
+#define PAGE_LEN 2
|
|
|
+#define DATA_BUF_LEN 32
|
|
|
+#define BUF_LEN 37
|
|
|
+#define QUERY_LEN 9
|
|
|
+#define DATA_LEN 12
|
|
|
+#define HAS_TAP 0x01
|
|
|
+#define HAS_PALMDETECT 0x01
|
|
|
+#define HAS_ROTATE 0x02
|
|
|
+#define HAS_TAPANDHOLD 0x02
|
|
|
+#define HAS_DOUBLETAP 0x04
|
|
|
+#define HAS_EARLYTAP 0x08
|
|
|
+#define HAS_RELEASE 0x08
|
|
|
+#define HAS_FLICK 0x10
|
|
|
+#define HAS_PRESS 0x20
|
|
|
+#define HAS_PINCH 0x40
|
|
|
+
|
|
|
+#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 TOUCHPAD_CTRL_INTR 0x8
|
|
|
+#define PDT_START_SCAN_LOCATION (0x00E9)
|
|
|
+#define PDT_END_SCAN_LOCATION (0x000A)
|
|
|
+#define PDT_ENTRY_SIZE (0x0006)
|
|
|
+#define RMI4_NUMBER_OF_MAX_FINGERS (8)
|
|
|
+#define SYNAPTICS_RMI4_TOUCHPAD_FUNC_NUM (0x11)
|
|
|
+#define SYNAPTICS_RMI4_DEVICE_CONTROL_FUNC_NUM (0x01)
|
|
|
+
|
|
|
+/**
|
|
|
+ * struct synaptics_rmi4_fn_desc - contains the funtion descriptor information
|
|
|
+ * @query_base_addr: base address for query
|
|
|
+ * @cmd_base_addr: base address for command
|
|
|
+ * @ctrl_base_addr: base address for control
|
|
|
+ * @data_base_addr: base address for data
|
|
|
+ * @intr_src_count: count for the interrupt source
|
|
|
+ * @fn_number: function number
|
|
|
+ *
|
|
|
+ * This structure is used to gives the function descriptor information
|
|
|
+ * of the particular functionality.
|
|
|
+ */
|
|
|
+struct synaptics_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;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * struct synaptics_rmi4_fn - contains the funtion information
|
|
|
+ * @fn_number: function number
|
|
|
+ * @num_of_data_sources: number of data sources
|
|
|
+ * @num_of_data_points: number of fingers touched
|
|
|
+ * @size_of_data_register_block: data register block size
|
|
|
+ * @index_to_intr_reg: index for interrupt register
|
|
|
+ * @intr_mask: interrupt mask value
|
|
|
+ * @fn_desc: variable for function descriptor structure
|
|
|
+ * @link: linked list for function descriptors
|
|
|
+ *
|
|
|
+ * This structure gives information about the number of data sources and
|
|
|
+ * the number of data registers associated with the function.
|
|
|
+ */
|
|
|
+struct synaptics_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_rmi4_fn_desc fn_desc;
|
|
|
+ struct list_head link;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * struct synaptics_rmi4_device_info - contains the rmi4 device information
|
|
|
+ * @version_major: protocol major version number
|
|
|
+ * @version_minor: protocol minor version number
|
|
|
+ * @manufacturer_id: manufacturer identification byte
|
|
|
+ * @product_props: product properties information
|
|
|
+ * @product_info: product info array
|
|
|
+ * @date_code: device manufacture date
|
|
|
+ * @tester_id: tester id array
|
|
|
+ * @serial_number: serial number for that device
|
|
|
+ * @product_id_string: product id for the device
|
|
|
+ * @support_fn_list: linked list for device information
|
|
|
+ *
|
|
|
+ * This structure gives information about the number of data sources and
|
|
|
+ * the number of data registers associated with the function.
|
|
|
+ */
|
|
|
+struct synaptics_rmi4_device_info {
|
|
|
+ unsigned int version_major;
|
|
|
+ unsigned int version_minor;
|
|
|
+ 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;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * struct synaptics_rmi4_data - contains the rmi4 device data
|
|
|
+ * @rmi4_mod_info: structure variable for rmi4 device info
|
|
|
+ * @input_dev: pointer for input device
|
|
|
+ * @i2c_client: pointer for i2c client
|
|
|
+ * @board: constant pointer for touch platform data
|
|
|
+ * @fn_list_mutex: mutex for funtion list
|
|
|
+ * @rmi4_page_mutex: mutex for rmi4 page
|
|
|
+ * @current_page: variable for integer
|
|
|
+ * @number_of_interrupt_register: interrupt registers count
|
|
|
+ * @fn01_ctrl_base_addr: control base address for fn01
|
|
|
+ * @fn01_query_base_addr: query base address for fn01
|
|
|
+ * @fn01_data_base_addr: data base address for fn01
|
|
|
+ * @sensor_max_x: sensor maximum x value
|
|
|
+ * @sensor_max_y: sensor maximum y value
|
|
|
+ * @regulator: pointer to the regulator structure
|
|
|
+ * @wait: wait queue structure variable
|
|
|
+ * @touch_stopped: flag to stop the thread function
|
|
|
+ *
|
|
|
+ * This structure gives the device data information.
|
|
|
+ */
|
|
|
+struct synaptics_rmi4_data {
|
|
|
+ struct synaptics_rmi4_device_info rmi4_mod_info;
|
|
|
+ struct input_dev *input_dev;
|
|
|
+ struct i2c_client *i2c_client;
|
|
|
+ const struct synaptics_rmi4_platform_data *board;
|
|
|
+ struct mutex fn_list_mutex;
|
|
|
+ struct mutex rmi4_page_mutex;
|
|
|
+ int current_page;
|
|
|
+ unsigned int number_of_interrupt_register;
|
|
|
+ unsigned short fn01_ctrl_base_addr;
|
|
|
+ unsigned short fn01_query_base_addr;
|
|
|
+ unsigned short fn01_data_base_addr;
|
|
|
+ int sensor_max_x;
|
|
|
+ int sensor_max_y;
|
|
|
+ struct regulator *regulator;
|
|
|
+ wait_queue_head_t wait;
|
|
|
+ bool touch_stopped;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * synaptics_rmi4_set_page() - sets the page
|
|
|
+ * @pdata: pointer to synaptics_rmi4_data structure
|
|
|
+ * @address: set the address of the page
|
|
|
+ *
|
|
|
+ * This function is used to set the page and returns integer.
|
|
|
+ */
|
|
|
+static int synaptics_rmi4_set_page(struct synaptics_rmi4_data *pdata,
|
|
|
+ unsigned int address)
|
|
|
+{
|
|
|
+ unsigned char txbuf[PAGE_LEN];
|
|
|
+ int retval;
|
|
|
+ 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)
|
|
|
+ dev_err(&i2c->dev, "%s:failed:%d\n", __func__, retval);
|
|
|
+ else
|
|
|
+ pdata->current_page = page;
|
|
|
+ } else
|
|
|
+ retval = PAGE_LEN;
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+/**
|
|
|
+ * synaptics_rmi4_i2c_block_read() - read the block of data
|
|
|
+ * @pdata: pointer to synaptics_rmi4_data structure
|
|
|
+ * @address: read the block of data from this offset
|
|
|
+ * @valp: pointer to a buffer containing the data to be read
|
|
|
+ * @size: number of bytes to read
|
|
|
+ *
|
|
|
+ * This function is to read the block of data and returns integer.
|
|
|
+ */
|
|
|
+static int synaptics_rmi4_i2c_block_read(struct synaptics_rmi4_data *pdata,
|
|
|
+ unsigned short address,
|
|
|
+ unsigned char *valp, int size)
|
|
|
+{
|
|
|
+ int retval = 0;
|
|
|
+ int retry_count = 0;
|
|
|
+ int index;
|
|
|
+ struct i2c_client *i2c = pdata->i2c_client;
|
|
|
+
|
|
|
+ mutex_lock(&(pdata->rmi4_page_mutex));
|
|
|
+ retval = synaptics_rmi4_set_page(pdata, address);
|
|
|
+ if (retval != PAGE_LEN)
|
|
|
+ goto exit;
|
|
|
+ index = address & MASK_8BIT;
|
|
|
+retry:
|
|
|
+ retval = i2c_smbus_read_i2c_block_data(i2c, index, size, valp);
|
|
|
+ if (retval != size) {
|
|
|
+ if (++retry_count == MAX_RETRY_COUNT)
|
|
|
+ dev_err(&i2c->dev,
|
|
|
+ "%s:address 0x%04x size %d failed:%d\n",
|
|
|
+ __func__, address, size, retval);
|
|
|
+ else {
|
|
|
+ synaptics_rmi4_set_page(pdata, address);
|
|
|
+ goto retry;
|
|
|
+ }
|
|
|
+ }
|
|
|
+exit:
|
|
|
+ mutex_unlock(&(pdata->rmi4_page_mutex));
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * synaptics_rmi4_i2c_byte_write() - write the single byte data
|
|
|
+ * @pdata: pointer to synaptics_rmi4_data structure
|
|
|
+ * @address: write the block of data from this offset
|
|
|
+ * @data: data to be write
|
|
|
+ *
|
|
|
+ * This function is to write the single byte data and returns integer.
|
|
|
+ */
|
|
|
+static int synaptics_rmi4_i2c_byte_write(struct synaptics_rmi4_data *pdata,
|
|
|
+ unsigned short address,
|
|
|
+ unsigned char data)
|
|
|
+{
|
|
|
+ unsigned char txbuf[2];
|
|
|
+ int retval = 0;
|
|
|
+ struct i2c_client *i2c = pdata->i2c_client;
|
|
|
+
|
|
|
+ /* Can't have anyone else changing the page behind our backs */
|
|
|
+ mutex_lock(&(pdata->rmi4_page_mutex));
|
|
|
+
|
|
|
+ retval = synaptics_rmi4_set_page(pdata, address);
|
|
|
+ if (retval != PAGE_LEN)
|
|
|
+ goto exit;
|
|
|
+ txbuf[0] = address & MASK_8BIT;
|
|
|
+ txbuf[1] = data;
|
|
|
+ retval = i2c_master_send(pdata->i2c_client, txbuf, 2);
|
|
|
+ /* Add in retry on writes only in certian error return values */
|
|
|
+ if (retval != 2) {
|
|
|
+ dev_err(&i2c->dev, "%s:failed:%d\n", __func__, retval);
|
|
|
+ retval = -EIO;
|
|
|
+ } else
|
|
|
+ retval = 1;
|
|
|
+exit:
|
|
|
+ mutex_unlock(&(pdata->rmi4_page_mutex));
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * synpatics_rmi4_touchpad_report() - reports for the rmi4 touchpad device
|
|
|
+ * @pdata: pointer to synaptics_rmi4_data structure
|
|
|
+ * @rfi: pointer to synaptics_rmi4_fn structure
|
|
|
+ *
|
|
|
+ * This function calls to reports for the rmi4 touchpad device
|
|
|
+ */
|
|
|
+static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata,
|
|
|
+ struct synaptics_rmi4_fn *rfi)
|
|
|
+{
|
|
|
+ /* number of touch points - fingers down in this case */
|
|
|
+ int touch_count = 0;
|
|
|
+ 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];
|
|
|
+ struct i2c_client *client = pdata->i2c_client;
|
|
|
+
|
|
|
+ /* 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 = rfi->num_of_data_points;
|
|
|
+ finger_registers = (fingers_supported + 3)/4;
|
|
|
+ data_base_addr = rfi->fn_desc.data_base_addr;
|
|
|
+ retval = synaptics_rmi4_i2c_block_read(pdata, data_base_addr, values,
|
|
|
+ finger_registers);
|
|
|
+ if (retval != finger_registers) {
|
|
|
+ dev_err(&client->dev, "%s:read status registers failed\n",
|
|
|
+ __func__);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * For each finger present, read the proper number of registers
|
|
|
+ * to get absolute data.
|
|
|
+ */
|
|
|
+ data_reg_blk_size = rfi->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_block_read(pdata,
|
|
|
+ data_offset, data,
|
|
|
+ data_reg_blk_size);
|
|
|
+ if (retval != data_reg_blk_size) {
|
|
|
+ printk(KERN_ERR "%s:read data failed\n",
|
|
|
+ __func__);
|
|
|
+ return 0;
|
|
|
+ } 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 the number of touch points */
|
|
|
+ return touch_count;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * synaptics_rmi4_report_device() - reports the rmi4 device
|
|
|
+ * @pdata: pointer to synaptics_rmi4_data structure
|
|
|
+ * @rfi: pointer to synaptics_rmi4_fn
|
|
|
+ *
|
|
|
+ * This function is used to call the report function of the rmi4 device.
|
|
|
+ */
|
|
|
+static int synaptics_rmi4_report_device(struct synaptics_rmi4_data *pdata,
|
|
|
+ struct synaptics_rmi4_fn *rfi)
|
|
|
+{
|
|
|
+ int touch = 0;
|
|
|
+ struct i2c_client *client = pdata->i2c_client;
|
|
|
+ static int num_error_reports;
|
|
|
+ if (rfi->fn_number != SYNAPTICS_RMI4_TOUCHPAD_FUNC_NUM) {
|
|
|
+ num_error_reports++;
|
|
|
+ if (num_error_reports < MAX_ERROR_REPORT)
|
|
|
+ dev_err(&client->dev, "%s:report not supported\n",
|
|
|
+ __func__);
|
|
|
+ } else
|
|
|
+ touch = synpatics_rmi4_touchpad_report(pdata, rfi);
|
|
|
+ return touch;
|
|
|
+}
|
|
|
+/**
|
|
|
+ * synaptics_rmi4_sensor_report() - reports to input subsystem
|
|
|
+ * @pdata: pointer to synaptics_rmi4_data structure
|
|
|
+ *
|
|
|
+ * This function is used to reads in all data sources and reports
|
|
|
+ * them to the input subsystem.
|
|
|
+ */
|
|
|
+static int synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *pdata)
|
|
|
+{
|
|
|
+ unsigned char intr_status[4];
|
|
|
+ /* number of touch points - fingers or buttons */
|
|
|
+ int touch = 0;
|
|
|
+ unsigned int retval;
|
|
|
+ struct synaptics_rmi4_fn *rfi;
|
|
|
+ struct synaptics_rmi4_device_info *rmi;
|
|
|
+ struct i2c_client *client = pdata->i2c_client;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 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_block_read(pdata,
|
|
|
+ pdata->fn01_data_base_addr + 1,
|
|
|
+ intr_status,
|
|
|
+ pdata->number_of_interrupt_register);
|
|
|
+ if (retval != pdata->number_of_interrupt_register) {
|
|
|
+ dev_err(&client->dev,
|
|
|
+ "could not read interrupt status registers\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(rfi, &rmi->support_fn_list, link) {
|
|
|
+ if (rfi->num_of_data_sources) {
|
|
|
+ if (intr_status[rfi->index_to_intr_reg] &
|
|
|
+ rfi->intr_mask)
|
|
|
+ touch = synaptics_rmi4_report_device(pdata,
|
|
|
+ rfi);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* return the number of touch points */
|
|
|
+ return touch;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * synaptics_rmi4_irq() - thread function for rmi4 attention line
|
|
|
+ * @irq: irq value
|
|
|
+ * @data: void pointer
|
|
|
+ *
|
|
|
+ * This function is interrupt thread function. It just notifies the
|
|
|
+ * application layer that attention is required.
|
|
|
+ */
|
|
|
+static irqreturn_t synaptics_rmi4_irq(int irq, void *data)
|
|
|
+{
|
|
|
+ struct synaptics_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;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * synpatics_rmi4_touchpad_detect() - detects the rmi4 touchpad device
|
|
|
+ * @pdata: pointer to synaptics_rmi4_data structure
|
|
|
+ * @rfi: pointer to synaptics_rmi4_fn structure
|
|
|
+ * @fd: pointer to synaptics_rmi4_fn_desc structure
|
|
|
+ * @interruptcount: count the number of interrupts
|
|
|
+ *
|
|
|
+ * This function calls to detects the rmi4 touchpad device
|
|
|
+ */
|
|
|
+static int synpatics_rmi4_touchpad_detect(struct synaptics_rmi4_data *pdata,
|
|
|
+ struct synaptics_rmi4_fn *rfi,
|
|
|
+ struct synaptics_rmi4_fn_desc *fd,
|
|
|
+ unsigned int interruptcount)
|
|
|
+{
|
|
|
+ unsigned char queries[QUERY_LEN];
|
|
|
+ unsigned short intr_offset;
|
|
|
+ unsigned char abs_data_size;
|
|
|
+ unsigned char abs_data_blk_size;
|
|
|
+ unsigned char egr_0, egr_1;
|
|
|
+ unsigned int all_data_blk_size;
|
|
|
+ int has_pinch, has_flick, has_tap;
|
|
|
+ int has_tapandhold, has_doubletap;
|
|
|
+ int has_earlytap, has_press;
|
|
|
+ int has_palmdetect, has_rotate;
|
|
|
+ int has_rel;
|
|
|
+ int i;
|
|
|
+ int retval;
|
|
|
+ struct i2c_client *client = pdata->i2c_client;
|
|
|
+
|
|
|
+ rfi->fn_desc.query_base_addr = fd->query_base_addr;
|
|
|
+ rfi->fn_desc.data_base_addr = fd->data_base_addr;
|
|
|
+ rfi->fn_desc.intr_src_count = fd->intr_src_count;
|
|
|
+ rfi->fn_desc.fn_number = fd->fn_number;
|
|
|
+ rfi->fn_number = fd->fn_number;
|
|
|
+ rfi->num_of_data_sources = fd->intr_src_count;
|
|
|
+ rfi->fn_desc.ctrl_base_addr = fd->ctrl_base_addr;
|
|
|
+ rfi->fn_desc.cmd_base_addr = fd->cmd_base_addr;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 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_block_read(pdata, fd->query_base_addr,
|
|
|
+ queries,
|
|
|
+ sizeof(queries));
|
|
|
+ if (retval != sizeof(queries)) {
|
|
|
+ dev_err(&client->dev, "%s:read function query registers\n",
|
|
|
+ __func__);
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * 2D data sources have only 3 bits for the number of fingers
|
|
|
+ * supported - so the encoding is a bit wierd.
|
|
|
+ */
|
|
|
+ if ((queries[1] & MASK_3BIT) <= 4)
|
|
|
+ /* add 1 since zero based */
|
|
|
+ rfi->num_of_data_points = (queries[1] & MASK_3BIT) + 1;
|
|
|
+ else {
|
|
|
+ /*
|
|
|
+ * a value of 5 is up to 10 fingers - 6 and 7 are reserved
|
|
|
+ * (shouldn't get these i int retval;n a normal 2D source).
|
|
|
+ */
|
|
|
+ if ((queries[1] & MASK_3BIT) == 5)
|
|
|
+ rfi->num_of_data_points = 10;
|
|
|
+ }
|
|
|
+ /* Need to get interrupt info for handling interrupts */
|
|
|
+ rfi->index_to_intr_reg = (interruptcount + 7)/8;
|
|
|
+ if (rfi->index_to_intr_reg != 0)
|
|
|
+ rfi->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;
|
|
|
+ rfi->intr_mask = 0;
|
|
|
+ for (i = intr_offset;
|
|
|
+ i < ((fd->intr_src_count & MASK_3BIT) + intr_offset); i++)
|
|
|
+ rfi->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));
|
|
|
+ rfi->size_of_data_register_block = abs_data_blk_size;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * need to determine the size of data to read - this depends on
|
|
|
+ * conditions such as whether Relative data is reported and if Gesture
|
|
|
+ * data is reported.
|
|
|
+ */
|
|
|
+ egr_0 = queries[7];
|
|
|
+ egr_1 = queries[8];
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Get info about what EGR data is supported, whether it has
|
|
|
+ * Relative data supported, etc.
|
|
|
+ */
|
|
|
+ has_pinch = egr_0 & HAS_PINCH;
|
|
|
+ has_flick = egr_0 & HAS_FLICK;
|
|
|
+ has_tap = egr_0 & HAS_TAP;
|
|
|
+ has_earlytap = egr_0 & HAS_EARLYTAP;
|
|
|
+ has_press = egr_0 & HAS_PRESS;
|
|
|
+ has_rotate = egr_1 & HAS_ROTATE;
|
|
|
+ has_rel = queries[1] & HAS_RELEASE;
|
|
|
+ has_tapandhold = egr_0 & HAS_TAPANDHOLD;
|
|
|
+ has_doubletap = egr_0 & HAS_DOUBLETAP;
|
|
|
+ has_palmdetect = egr_1 & HAS_PALMDETECT;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Size of all data including finger status, absolute data for each
|
|
|
+ * finger, relative data and EGR data
|
|
|
+ */
|
|
|
+ all_data_blk_size =
|
|
|
+ /* finger status, four fingers per register */
|
|
|
+ ((rfi->num_of_data_points + 3) / 4) +
|
|
|
+ /* absolute data, per finger times number of fingers */
|
|
|
+ (abs_data_blk_size * rfi->num_of_data_points) +
|
|
|
+ /*
|
|
|
+ * two relative registers (if relative is being reported)
|
|
|
+ */
|
|
|
+ 2 * has_rel +
|
|
|
+ /*
|
|
|
+ * F11_2D_data8 is only present if the egr_0
|
|
|
+ * register is non-zero.
|
|
|
+ */
|
|
|
+ !!(egr_0) +
|
|
|
+ /*
|
|
|
+ * F11_2D_data9 is only present if either egr_0 or
|
|
|
+ * egr_1 registers are non-zero.
|
|
|
+ */
|
|
|
+ (egr_0 || egr_1) +
|
|
|
+ /*
|
|
|
+ * F11_2D_data10 is only present if EGR_PINCH or EGR_FLICK of
|
|
|
+ * egr_0 reports as 1.
|
|
|
+ */
|
|
|
+ !!(has_pinch | has_flick) +
|
|
|
+ /*
|
|
|
+ * F11_2D_data11 and F11_2D_data12 are only present if
|
|
|
+ * EGR_FLICK of egr_0 reports as 1.
|
|
|
+ */
|
|
|
+ 2 * !!(has_flick);
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * synpatics_rmi4_touchpad_config() - confiures the rmi4 touchpad device
|
|
|
+ * @pdata: pointer to synaptics_rmi4_data structure
|
|
|
+ * @rfi: pointer to synaptics_rmi4_fn structure
|
|
|
+ *
|
|
|
+ * This function calls to confiures the rmi4 touchpad device
|
|
|
+ */
|
|
|
+int synpatics_rmi4_touchpad_config(struct synaptics_rmi4_data *pdata,
|
|
|
+ struct synaptics_rmi4_fn *rfi)
|
|
|
+{
|
|
|
+ /*
|
|
|
+ * For the data source - print info and do any
|
|
|
+ * source specific configuration.
|
|
|
+ */
|
|
|
+ unsigned char data[BUF_LEN];
|
|
|
+ int retval = 0;
|
|
|
+ struct i2c_client *client = pdata->i2c_client;
|
|
|
+
|
|
|
+ /* Get and print some info about the data source... */
|
|
|
+ /* To Query 2D devices we need to read from the address obtained
|
|
|
+ * from the function descriptor stored in the RMI function info.
|
|
|
+ */
|
|
|
+ retval = synaptics_rmi4_i2c_block_read(pdata,
|
|
|
+ rfi->fn_desc.query_base_addr,
|
|
|
+ data, QUERY_LEN);
|
|
|
+ if (retval != QUERY_LEN)
|
|
|
+ dev_err(&client->dev, "%s:read query registers failed\n",
|
|
|
+ __func__);
|
|
|
+ else {
|
|
|
+ retval = synaptics_rmi4_i2c_block_read(pdata,
|
|
|
+ rfi->fn_desc.ctrl_base_addr,
|
|
|
+ data, DATA_BUF_LEN);
|
|
|
+ if (retval != DATA_BUF_LEN) {
|
|
|
+ dev_err(&client->dev,
|
|
|
+ "%s:read control registers failed\n",
|
|
|
+ __func__);
|
|
|
+ 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_5BIT) << 0) |
|
|
|
+ ((data[9] & MASK_4BIT) << 8);
|
|
|
+ }
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * synaptics_rmi4_i2c_query_device() - query the rmi4 device
|
|
|
+ * @pdata: pointer to synaptics_rmi4_data structure
|
|
|
+ *
|
|
|
+ * This function is used to query the rmi4 device.
|
|
|
+ */
|
|
|
+static int synaptics_rmi4_i2c_query_device(struct synaptics_rmi4_data *pdata)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ int retval;
|
|
|
+ unsigned char std_queries[STD_QUERY_LEN];
|
|
|
+ unsigned char intr_count = 0;
|
|
|
+ int data_sources = 0;
|
|
|
+ unsigned int ctrl_offset;
|
|
|
+ struct synaptics_rmi4_fn *rfi;
|
|
|
+ struct synaptics_rmi4_fn_desc rmi_fd;
|
|
|
+ struct synaptics_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);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Read the Page Descriptor Table to determine what functions
|
|
|
+ * are present
|
|
|
+ */
|
|
|
+ for (i = PDT_START_SCAN_LOCATION; i > PDT_END_SCAN_LOCATION;
|
|
|
+ i -= PDT_ENTRY_SIZE) {
|
|
|
+ retval = synaptics_rmi4_i2c_block_read(pdata, i,
|
|
|
+ (unsigned char *)&rmi_fd,
|
|
|
+ sizeof(rmi_fd));
|
|
|
+ if (retval != sizeof(rmi_fd)) {
|
|
|
+ /* failed to read next PDT entry */
|
|
|
+ dev_err(&client->dev, "%s: read error\n", __func__);
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
+ rfi = NULL;
|
|
|
+ if (rmi_fd.fn_number) {
|
|
|
+ switch (rmi_fd.fn_number & MASK_8BIT) {
|
|
|
+ case SYNAPTICS_RMI4_DEVICE_CONTROL_FUNC_NUM:
|
|
|
+ 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_RMI4_TOUCHPAD_FUNC_NUM:
|
|
|
+ if (rmi_fd.intr_src_count) {
|
|
|
+ rfi = kmalloc(sizeof(*rfi),
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!rfi) {
|
|
|
+ dev_err(&client->dev,
|
|
|
+ "%s:kmalloc failed\n",
|
|
|
+ __func__);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+ retval = synpatics_rmi4_touchpad_detect
|
|
|
+ (pdata, rfi,
|
|
|
+ &rmi_fd,
|
|
|
+ intr_count);
|
|
|
+ if (retval < 0)
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ /* interrupt count for next iteration */
|
|
|
+ intr_count += (rmi_fd.intr_src_count & MASK_3BIT);
|
|
|
+ /*
|
|
|
+ * We only want to add functions to the list
|
|
|
+ * that have data associated with them.
|
|
|
+ */
|
|
|
+ if (rfi && rmi_fd.intr_src_count) {
|
|
|
+ /* link this function info to the RMI module */
|
|
|
+ mutex_lock(&(pdata->fn_list_mutex));
|
|
|
+ list_add_tail(&rfi->link,
|
|
|
+ &pdata->rmi4_mod_info.support_fn_list);
|
|
|
+ mutex_unlock(&(pdata->fn_list_mutex));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * A zero in the function number
|
|
|
+ * signals the end of the PDT
|
|
|
+ */
|
|
|
+ dev_dbg(&client->dev,
|
|
|
+ "%s:end of PDT\n", __func__);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * 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;
|
|
|
+ /*
|
|
|
+ * Function $01 will be used to query the product properties,
|
|
|
+ * and product ID so we had to read the PDT above first to get
|
|
|
+ * the Fn $01 query address and prior to filling in the product
|
|
|
+ * info. NOTE: Even an unflashed device will still have FN $01.
|
|
|
+ */
|
|
|
+
|
|
|
+ /* Load up the standard queries and get the RMI4 module info */
|
|
|
+ retval = synaptics_rmi4_i2c_block_read(pdata,
|
|
|
+ pdata->fn01_query_base_addr,
|
|
|
+ std_queries,
|
|
|
+ sizeof(std_queries));
|
|
|
+ if (retval != sizeof(std_queries)) {
|
|
|
+ dev_err(&client->dev, "%s:Failed reading queries\n",
|
|
|
+ __func__);
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Currently supported RMI version is 4.0 */
|
|
|
+ pdata->rmi4_mod_info.version_major = 4;
|
|
|
+ pdata->rmi4_mod_info.version_minor = 0;
|
|
|
+ /*
|
|
|
+ * 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);
|
|
|
+
|
|
|
+ list_for_each_entry(rfi, &pdata->rmi4_mod_info.support_fn_list, link)
|
|
|
+ data_sources += rfi->num_of_data_sources;
|
|
|
+ if (data_sources) {
|
|
|
+ rmi = &(pdata->rmi4_mod_info);
|
|
|
+ list_for_each_entry(rfi, &rmi->support_fn_list, link) {
|
|
|
+ if (rfi->num_of_data_sources) {
|
|
|
+ if (rfi->fn_number ==
|
|
|
+ SYNAPTICS_RMI4_TOUCHPAD_FUNC_NUM) {
|
|
|
+ retval = synpatics_rmi4_touchpad_config
|
|
|
+ (pdata, rfi);
|
|
|
+ if (retval < 0)
|
|
|
+ return retval;
|
|
|
+ } else
|
|
|
+ dev_err(&client->dev,
|
|
|
+ "%s:fn_number not supported\n",
|
|
|
+ __func__);
|
|
|
+ /*
|
|
|
+ * Turn on interrupts for this
|
|
|
+ * function's data sources.
|
|
|
+ */
|
|
|
+ ctrl_offset = pdata->fn01_ctrl_base_addr + 1 +
|
|
|
+ rfi->index_to_intr_reg;
|
|
|
+ retval = synaptics_rmi4_i2c_byte_write(pdata,
|
|
|
+ ctrl_offset,
|
|
|
+ rfi->intr_mask);
|
|
|
+ if (retval < 0)
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * synaptics_rmi4_probe() - Initialze the i2c-client touchscreen driver
|
|
|
+ * @i2c: i2c client structure pointer
|
|
|
+ * @id:i2c device id pointer
|
|
|
+ *
|
|
|
+ * This function will allocate and initialize the instance
|
|
|
+ * data and request the irq and set the instance data as the clients
|
|
|
+ * platform data then register the physical driver which will do a scan of
|
|
|
+ * the rmi4 Physical Device Table and enumerate any rmi4 functions that
|
|
|
+ * have data sources associated with them.
|
|
|
+ */
|
|
|
+static int __devinit synaptics_rmi4_probe
|
|
|
+ (struct i2c_client *client, const struct i2c_device_id *dev_id)
|
|
|
+{
|
|
|
+ int retval;
|
|
|
+ unsigned char intr_status[4];
|
|
|
+ struct synaptics_rmi4_data *rmi4_data;
|
|
|
+ const struct synaptics_rmi4_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 */
|
|
|
+ rmi4_data = kzalloc(sizeof(struct synaptics_rmi4_data) * 2,
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!rmi4_data) {
|
|
|
+ dev_err(&client->dev, "%s: no memory allocated\n", __func__);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ rmi4_data->input_dev = input_allocate_device();
|
|
|
+ if (rmi4_data->input_dev == NULL) {
|
|
|
+ dev_err(&client->dev, "%s:input device alloc failed\n",
|
|
|
+ __func__);
|
|
|
+ retval = -ENOMEM;
|
|
|
+ goto err_input;
|
|
|
+ }
|
|
|
+
|
|
|
+ dev_set_name(&client->dev, platformdata->name);
|
|
|
+
|
|
|
+ if (platformdata->regulator_en) {
|
|
|
+ rmi4_data->regulator = regulator_get(&client->dev, "v-touch");
|
|
|
+ if (IS_ERR(rmi4_data->regulator)) {
|
|
|
+ dev_err(&client->dev, "%s:get regulator failed\n",
|
|
|
+ __func__);
|
|
|
+ retval = PTR_ERR(rmi4_data->regulator);
|
|
|
+ goto err_regulator;
|
|
|
+ }
|
|
|
+ regulator_enable(rmi4_data->regulator);
|
|
|
+ }
|
|
|
+
|
|
|
+ init_waitqueue_head(&rmi4_data->wait);
|
|
|
+ /*
|
|
|
+ * Copy i2c_client pointer into RTID's i2c_client pointer for
|
|
|
+ * later use in rmi4_read, rmi4_write, etc.
|
|
|
+ */
|
|
|
+ rmi4_data->i2c_client = client;
|
|
|
+ /* So we set the page correctly the first time */
|
|
|
+ rmi4_data->current_page = MASK_16BIT;
|
|
|
+ rmi4_data->board = platformdata;
|
|
|
+ rmi4_data->touch_stopped = false;
|
|
|
+
|
|
|
+ /* init the mutexes for maintain the lists */
|
|
|
+ mutex_init(&(rmi4_data->fn_list_mutex));
|
|
|
+ mutex_init(&(rmi4_data->rmi4_page_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_i2c_query_device(rmi4_data);
|
|
|
+ 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, rmi4_data);
|
|
|
+
|
|
|
+ /*initialize the input device parameters */
|
|
|
+ rmi4_data->input_dev->name = DRIVER_NAME;
|
|
|
+ rmi4_data->input_dev->phys = "Synaptics_Clearpad";
|
|
|
+ rmi4_data->input_dev->id.bustype = BUS_I2C;
|
|
|
+ rmi4_data->input_dev->dev.parent = &client->dev;
|
|
|
+ input_set_drvdata(rmi4_data->input_dev, rmi4_data);
|
|
|
+
|
|
|
+ /* Initialize the function handlers for rmi4 */
|
|
|
+ set_bit(EV_SYN, rmi4_data->input_dev->evbit);
|
|
|
+ set_bit(EV_KEY, rmi4_data->input_dev->evbit);
|
|
|
+ set_bit(EV_ABS, rmi4_data->input_dev->evbit);
|
|
|
+
|
|
|
+ input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_X, 0,
|
|
|
+ rmi4_data->sensor_max_x, 0, 0);
|
|
|
+ input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_Y, 0,
|
|
|
+ rmi4_data->sensor_max_y, 0, 0);
|
|
|
+ input_set_abs_params(rmi4_data->input_dev, ABS_MT_TOUCH_MAJOR, 0,
|
|
|
+ MAX_TOUCH_MAJOR, 0, 0);
|
|
|
+
|
|
|
+ retval = input_register_device(rmi4_data->input_dev);
|
|
|
+ if (retval) {
|
|
|
+ dev_err(&client->dev, "%s:input register failed\n", __func__);
|
|
|
+ goto err_input_register;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Clear interrupts */
|
|
|
+ synaptics_rmi4_i2c_block_read(rmi4_data,
|
|
|
+ rmi4_data->fn01_data_base_addr + 1, intr_status,
|
|
|
+ rmi4_data->number_of_interrupt_register);
|
|
|
+ retval = request_threaded_irq(platformdata->irq_number, NULL,
|
|
|
+ synaptics_rmi4_irq,
|
|
|
+ platformdata->irq_type,
|
|
|
+ platformdata->name, rmi4_data);
|
|
|
+ if (retval) {
|
|
|
+ dev_err(&client->dev, "%s:Unable to get attn irq %d\n",
|
|
|
+ __func__, platformdata->irq_number);
|
|
|
+ goto err_request_irq;
|
|
|
+ }
|
|
|
+
|
|
|
+ return retval;
|
|
|
+
|
|
|
+err_request_irq:
|
|
|
+ free_irq(platformdata->irq_number, rmi4_data);
|
|
|
+ input_unregister_device(rmi4_data->input_dev);
|
|
|
+err_input_register:
|
|
|
+ i2c_set_clientdata(client, NULL);
|
|
|
+err_query_dev:
|
|
|
+ if (platformdata->regulator_en) {
|
|
|
+ regulator_disable(rmi4_data->regulator);
|
|
|
+ regulator_put(rmi4_data->regulator);
|
|
|
+ }
|
|
|
+err_regulator:
|
|
|
+ input_free_device(rmi4_data->input_dev);
|
|
|
+ rmi4_data->input_dev = NULL;
|
|
|
+err_input:
|
|
|
+ kfree(rmi4_data);
|
|
|
+
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+/**
|
|
|
+ * synaptics_rmi4_remove() - Removes the i2c-client touchscreen driver
|
|
|
+ * @client: i2c client structure pointer
|
|
|
+ *
|
|
|
+ * This funtion uses to remove the i2c-client
|
|
|
+ * touchscreen driver and returns integer.
|
|
|
+ */
|
|
|
+static int __devexit synaptics_rmi4_remove(struct i2c_client *client)
|
|
|
+{
|
|
|
+ struct synaptics_rmi4_data *rmi4_data = i2c_get_clientdata(client);
|
|
|
+ const struct synaptics_rmi4_platform_data *pdata = rmi4_data->board;
|
|
|
+
|
|
|
+ rmi4_data->touch_stopped = true;
|
|
|
+ wake_up(&rmi4_data->wait);
|
|
|
+ free_irq(pdata->irq_number, rmi4_data);
|
|
|
+ input_unregister_device(rmi4_data->input_dev);
|
|
|
+ if (pdata->regulator_en) {
|
|
|
+ regulator_disable(rmi4_data->regulator);
|
|
|
+ regulator_put(rmi4_data->regulator);
|
|
|
+ }
|
|
|
+ kfree(rmi4_data);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef CONFIG_PM
|
|
|
+/**
|
|
|
+ * synaptics_rmi4_suspend() - suspend the touch screen controller
|
|
|
+ * @dev: pointer to device structure
|
|
|
+ *
|
|
|
+ * This funtion is used to suspend the
|
|
|
+ * touch panel controller and returns integer
|
|
|
+ */
|
|
|
+static int synaptics_rmi4_suspend(struct device *dev)
|
|
|
+{
|
|
|
+ /* Touch sleep mode */
|
|
|
+ int retval;
|
|
|
+ unsigned char intr_status;
|
|
|
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
|
|
|
+ const struct synaptics_rmi4_platform_data *pdata = rmi4_data->board;
|
|
|
+
|
|
|
+ rmi4_data->touch_stopped = true;
|
|
|
+ disable_irq(pdata->irq_number);
|
|
|
+
|
|
|
+ retval = synaptics_rmi4_i2c_block_read(rmi4_data,
|
|
|
+ rmi4_data->fn01_data_base_addr + 1,
|
|
|
+ &intr_status,
|
|
|
+ rmi4_data->number_of_interrupt_register);
|
|
|
+ if (retval < 0)
|
|
|
+ return retval;
|
|
|
+
|
|
|
+ retval = synaptics_rmi4_i2c_byte_write(rmi4_data,
|
|
|
+ rmi4_data->fn01_ctrl_base_addr + 1,
|
|
|
+ (intr_status & ~TOUCHPAD_CTRL_INTR));
|
|
|
+ if (retval < 0)
|
|
|
+ return retval;
|
|
|
+
|
|
|
+ if (pdata->regulator_en)
|
|
|
+ regulator_disable(rmi4_data->regulator);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+/**
|
|
|
+ * synaptics_rmi4_resume() - resume the touch screen controller
|
|
|
+ * @dev: pointer to device structure
|
|
|
+ *
|
|
|
+ * This funtion is used to resume the touch panel
|
|
|
+ * controller and returns integer.
|
|
|
+ */
|
|
|
+static int synaptics_rmi4_resume(struct device *dev)
|
|
|
+{
|
|
|
+ int retval;
|
|
|
+ unsigned char intr_status;
|
|
|
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
|
|
|
+ const struct synaptics_rmi4_platform_data *pdata = rmi4_data->board;
|
|
|
+
|
|
|
+ if (pdata->regulator_en)
|
|
|
+ regulator_enable(rmi4_data->regulator);
|
|
|
+
|
|
|
+ enable_irq(pdata->irq_number);
|
|
|
+ rmi4_data->touch_stopped = false;
|
|
|
+
|
|
|
+ retval = synaptics_rmi4_i2c_block_read(rmi4_data,
|
|
|
+ rmi4_data->fn01_data_base_addr + 1,
|
|
|
+ &intr_status,
|
|
|
+ rmi4_data->number_of_interrupt_register);
|
|
|
+ if (retval < 0)
|
|
|
+ return retval;
|
|
|
+
|
|
|
+ retval = synaptics_rmi4_i2c_byte_write(rmi4_data,
|
|
|
+ rmi4_data->fn01_ctrl_base_addr + 1,
|
|
|
+ (intr_status | TOUCHPAD_CTRL_INTR));
|
|
|
+ if (retval < 0)
|
|
|
+ return retval;
|
|
|
+
|
|
|
+ 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,
|
|
|
+};
|
|
|
+/**
|
|
|
+ * synaptics_rmi4_init() - Initialize the touchscreen driver
|
|
|
+ *
|
|
|
+ * This funtion uses to initializes the synaptics
|
|
|
+ * touchscreen driver and returns integer.
|
|
|
+ */
|
|
|
+static int __init synaptics_rmi4_init(void)
|
|
|
+{
|
|
|
+ return i2c_add_driver(&synaptics_rmi4_driver);
|
|
|
+}
|
|
|
+/**
|
|
|
+ * synaptics_rmi4_exit() - De-initialize the touchscreen driver
|
|
|
+ *
|
|
|
+ * This funtion uses to de-initialize the synaptics
|
|
|
+ * touchscreen driver and returns none.
|
|
|
+ */
|
|
|
+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 v2");
|
|
|
+MODULE_AUTHOR("naveen.gaddipati@stericsson.com, js.ha@stericsson.com");
|
|
|
+MODULE_DESCRIPTION("synaptics rmi4 i2c touch Driver");
|
|
|
+MODULE_ALIAS("i2c:synaptics_rmi4_ts");
|