input: touchscreen: synaptics_dsx: add new touch driver

This is the reference driver source code for Synaptics touch driver.

Change-Id: Id35f12526ba8d8dfe114a0ec337060e7df9877bb
Signed-off-by: xigualu <luxh0702@thundersoft.com>
Git-commit: 648ee265de0dc2d852719b730fd1dfab153f7f55
Git-repo: https://source.codeaurora.org/external/thundersoft/ihvjointlab/sensor-driver/commit/?h=Synaptic_dsx
Signed-off-by: Xin Hua Lu <luxinhua@codeaurora.org>
This commit is contained in:
xigualu 2019-06-20 10:27:36 +08:00 • committed by Gerrit - the friendly Code Review server
commit e5cd7b281b
15 changed files with 24383 additions and 0 deletions

View file

@ -0,0 +1,142 @@
#
# Synaptics DSX touchscreen driver configuration
#
menuconfig TOUCHSCREEN_SYNAPTICS_DSX
bool "Synaptics DSX touchscreen"
help
Say Y here if you have a Synaptics DSX touchscreen connected
to your system.
If unsure, say N.
if TOUCHSCREEN_SYNAPTICS_DSX
choice
default TOUCHSCREEN_SYNAPTICS_DSX_I2C
prompt "Synaptics DSX bus interface"
config TOUCHSCREEN_SYNAPTICS_DSX_I2C
bool "RMI over I2C"
depends on I2C
config TOUCHSCREEN_SYNAPTICS_DSX_SPI
bool "RMI over SPI"
depends on SPI_MASTER
config TOUCHSCREEN_SYNAPTICS_DSX_RMI_HID_I2C
bool "HID over I2C"
depends on I2C
endchoice
config TOUCHSCREEN_SYNAPTICS_DSX_CORE
tristate "Synaptics DSX core driver module"
depends on I2C || SPI_MASTER
default y
help
Say Y here to enable basic touch reporting functionality.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_core.
config TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV
tristate "Synaptics DSX RMI device module"
depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
default y
help
Say Y here to enable support for direct RMI register access.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_rmi_dev.
config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE
tristate "Synaptics DSX firmware update module"
depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
default y
help
Say Y here to enable support for doing firmware update.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_fw_update.
config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
bool "Synaptics DSX firmware update sysfs attributes"
depends on TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE
default y
help
Say Y here to enable support for sysfs attributes for
performing firmware update in a development environment.
This does not affect the core or other subsystem attributes.
If unsure, say N.
config TOUCHSCREEN_SYNAPTICS_DSX_TEST_REPORTING
tristate "Synaptics DSX test reporting module"
depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
default y
help
Say Y here to enable support for retrieving production test reports.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_test_reporting.
config TOUCHSCREEN_SYNAPTICS_DSX_PROXIMITY
tristate "Synaptics DSX proximity module"
depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
help
Say Y here to enable support for proximity functionality.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_proximity.
config TOUCHSCREEN_SYNAPTICS_DSX_ACTIVE_PEN
tristate "Synaptics DSX active pen module"
depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
help
Say Y here to enable support for active pen functionality.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_active_pen.
config TOUCHSCREEN_SYNAPTICS_DSX_GESTURE
tristate "Synaptics DSX user defined gesture module"
depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
help
Say Y here to enable support for user defined gesture functionality.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_gesture.
config TOUCHSCREEN_SYNAPTICS_DSX_VIDEO
tristate "Synaptics DSX video module"
depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
help
Say Y here to enable support for video communication functionality.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_video.
config TOUCHSCREEN_SYNAPTICS_DSX_DEBUG
tristate "Synaptics DSX debug module"
depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
help
Say Y here to enable support for firmware debug functionality.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_debug.
endif

View file

@ -0,0 +1,18 @@
#
# Makefile for the Synaptics DSX touchscreen driver.
#
# Each configuration option enables a list of files.
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_I2C) += synaptics_dsx_i2c.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_SPI) += synaptics_dsx_spi.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_HID_I2C) += synaptics_dsx_rmi_hid_i2c.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE) += synaptics_dsx_core.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV) += synaptics_dsx_rmi_dev.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE) += synaptics_dsx_fw_update.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_TEST_REPORTING) += synaptics_dsx_test_reporting.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_PROXIMITY) += synaptics_dsx_proximity.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_ACTIVE_PEN) += synaptics_dsx_active_pen.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_GESTURE) += synaptics_dsx_gesture.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_VIDEO) += synaptics_dsx_video.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_DEBUG) += synaptics_dsx_debug.o

View file

@ -0,0 +1,608 @@
/*
* Synaptics DSX touchscreen driver
*
* Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved.
*
* Copyright (c) 2018 The Linux Foundation. All rights reserved.
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
*
* 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.
*
* INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
* EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
* AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
* IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
* WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
* AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
* NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
* TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
* DOLLARS.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/input/synaptics_dsx.h>
#include "synaptics_dsx_core.h"
#define APEN_PHYS_NAME "synaptics_dsx/active_pen"
#define ACTIVE_PEN_MAX_PRESSURE_16BIT 65535
#define ACTIVE_PEN_MAX_PRESSURE_8BIT 255
struct synaptics_rmi4_f12_query_8 {
union {
struct {
unsigned char size_of_query9;
struct {
unsigned char data0_is_present:1;
unsigned char data1_is_present:1;
unsigned char data2_is_present:1;
unsigned char data3_is_present:1;
unsigned char data4_is_present:1;
unsigned char data5_is_present:1;
unsigned char data6_is_present:1;
unsigned char data7_is_present:1;
} __packed;
};
unsigned char data[2];
};
};
struct apen_data_8b_pressure {
union {
struct {
unsigned char status_pen:1;
unsigned char status_invert:1;
unsigned char status_barrel:1;
unsigned char status_reserved:5;
unsigned char x_lsb;
unsigned char x_msb;
unsigned char y_lsb;
unsigned char y_msb;
unsigned char pressure_msb;
unsigned char battery_state;
unsigned char pen_id_0_7;
unsigned char pen_id_8_15;
unsigned char pen_id_16_23;
unsigned char pen_id_24_31;
} __packed;
unsigned char data[11];
};
};
struct apen_data {
union {
struct {
unsigned char status_pen:1;
unsigned char status_invert:1;
unsigned char status_barrel:1;
unsigned char status_reserved:5;
unsigned char x_lsb;
unsigned char x_msb;
unsigned char y_lsb;
unsigned char y_msb;
unsigned char pressure_lsb;
unsigned char pressure_msb;
unsigned char battery_state;
unsigned char pen_id_0_7;
unsigned char pen_id_8_15;
unsigned char pen_id_16_23;
unsigned char pen_id_24_31;
} __packed;
unsigned char data[12];
};
};
struct synaptics_rmi4_apen_handle {
bool apen_present;
unsigned char intr_mask;
unsigned char battery_state;
unsigned short query_base_addr;
unsigned short control_base_addr;
unsigned short data_base_addr;
unsigned short command_base_addr;
unsigned short apen_data_addr;
unsigned short max_pressure;
unsigned int pen_id;
struct input_dev *apen_dev;
struct apen_data *apen_data;
struct synaptics_rmi4_data *rmi4_data;
};
static struct synaptics_rmi4_apen_handle *apen;
DECLARE_COMPLETION(apen_remove_complete);
static void apen_lift(void)
{
input_report_key(apen->apen_dev, BTN_TOUCH, 0);
input_report_key(apen->apen_dev, BTN_TOOL_PEN, 0);
input_report_key(apen->apen_dev, BTN_TOOL_RUBBER, 0);
input_sync(apen->apen_dev);
apen->apen_present = false;
}
static void apen_report(void)
{
int retval;
int x;
int y;
int pressure;
static int invert = -1;
struct apen_data_8b_pressure *apen_data_8b;
struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data;
retval = synaptics_rmi4_reg_read(rmi4_data,
apen->apen_data_addr,
apen->apen_data->data,
sizeof(apen->apen_data->data));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read active pen data\n",
__func__);
return;
}
if (apen->apen_data->status_pen == 0) {
if (apen->apen_present)
apen_lift();
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: No active pen data\n",
__func__);
return;
}
x = (apen->apen_data->x_msb << 8) | (apen->apen_data->x_lsb);
y = (apen->apen_data->y_msb << 8) | (apen->apen_data->y_lsb);
if ((x == -1) && (y == -1)) {
if (apen->apen_present)
apen_lift();
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Active pen in range but no valid x & y\n",
__func__);
return;
}
if (!apen->apen_present)
invert = -1;
if (invert != -1 && invert != apen->apen_data->status_invert)
apen_lift();
invert = apen->apen_data->status_invert;
if (apen->max_pressure == ACTIVE_PEN_MAX_PRESSURE_16BIT) {
pressure = (apen->apen_data->pressure_msb << 8) |
apen->apen_data->pressure_lsb;
apen->battery_state = apen->apen_data->battery_state;
apen->pen_id = (apen->apen_data->pen_id_24_31 << 24) |
(apen->apen_data->pen_id_16_23 << 16) |
(apen->apen_data->pen_id_8_15 << 8) |
apen->apen_data->pen_id_0_7;
} else {
apen_data_8b = (struct apen_data_8b_pressure *)apen->apen_data;
pressure = apen_data_8b->pressure_msb;
apen->battery_state = apen_data_8b->battery_state;
apen->pen_id = (apen_data_8b->pen_id_24_31 << 24) |
(apen_data_8b->pen_id_16_23 << 16) |
(apen_data_8b->pen_id_8_15 << 8) |
apen_data_8b->pen_id_0_7;
}
input_report_key(apen->apen_dev, BTN_TOUCH, pressure > 0 ? 1 : 0);
input_report_key(apen->apen_dev,
apen->apen_data->status_invert > 0 ?
BTN_TOOL_RUBBER : BTN_TOOL_PEN, 1);
input_report_key(apen->apen_dev,
BTN_STYLUS, apen->apen_data->status_barrel > 0 ?
1 : 0);
input_report_abs(apen->apen_dev, ABS_X, x);
input_report_abs(apen->apen_dev, ABS_Y, y);
input_report_abs(apen->apen_dev, ABS_PRESSURE, pressure);
input_sync(apen->apen_dev);
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Active pen: status = %d, invert = %d, barrel = %d, x = %d, y = %d, pressure = %d\n",
__func__,
apen->apen_data->status_pen,
apen->apen_data->status_invert,
apen->apen_data->status_barrel,
x, y, pressure);
apen->apen_present = true;
}
static void apen_set_params(void)
{
input_set_abs_params(apen->apen_dev, ABS_X, 0,
apen->rmi4_data->sensor_max_x, 0, 0);
input_set_abs_params(apen->apen_dev, ABS_Y, 0,
apen->rmi4_data->sensor_max_y, 0, 0);
input_set_abs_params(apen->apen_dev, ABS_PRESSURE, 0,
apen->max_pressure, 0, 0);
return;
}
static int apen_pressure(struct synaptics_rmi4_f12_query_8 *query_8)
{
int retval;
unsigned char ii;
unsigned char data_reg_presence;
unsigned char size_of_query_9;
unsigned char *query_9;
unsigned char *data_desc;
struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data;
data_reg_presence = query_8->data[1];
size_of_query_9 = query_8->size_of_query9;
query_9 = kmalloc(size_of_query_9, GFP_KERNEL);
retval = synaptics_rmi4_reg_read(rmi4_data,
apen->query_base_addr + 9,
query_9,
size_of_query_9);
if (retval < 0)
goto exit;
data_desc = query_9;
for (ii = 0; ii < 6; ii++) {
if (!(data_reg_presence & (1 << ii)))
continue; /* The data register is not present */
data_desc++; /* Jump over the size entry */
while (*data_desc & (1 << 7))
data_desc++;
data_desc++; /* Go to the next descriptor */
}
data_desc++; /* Jump over the size entry */
/* Check for the presence of subpackets 1 and 2 */
if ((*data_desc & (3 << 1)) == (3 << 1))
apen->max_pressure = ACTIVE_PEN_MAX_PRESSURE_16BIT;
else
apen->max_pressure = ACTIVE_PEN_MAX_PRESSURE_8BIT;
exit:
kfree(query_9);
return retval;
}
static int apen_reg_init(void)
{
int retval;
unsigned char data_offset;
unsigned char size_of_query8;
struct synaptics_rmi4_f12_query_8 query_8;
struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data;
retval = synaptics_rmi4_reg_read(rmi4_data,
apen->query_base_addr + 7,
&size_of_query8,
sizeof(size_of_query8));
if (retval < 0)
return retval;
retval = synaptics_rmi4_reg_read(rmi4_data,
apen->query_base_addr + 8,
query_8.data,
sizeof(query_8.data));
if (retval < 0)
return retval;
if ((size_of_query8 >= 2) && (query_8.data6_is_present)) {
data_offset = query_8.data0_is_present +
query_8.data1_is_present +
query_8.data2_is_present +
query_8.data3_is_present +
query_8.data4_is_present +
query_8.data5_is_present;
apen->apen_data_addr = apen->data_base_addr + data_offset;
retval = apen_pressure(&query_8);
if (retval < 0)
return retval;
} else {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Active pen support unavailable\n",
__func__);
retval = -ENODEV;
}
return retval;
}
static int apen_scan_pdt(void)
{
int retval;
unsigned char ii;
unsigned char page;
unsigned char intr_count = 0;
unsigned char intr_off;
unsigned char intr_src;
unsigned short addr;
struct synaptics_rmi4_fn_desc fd;
struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data;
for (page = 0; page < PAGES_TO_SERVICE; page++) {
for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) {
addr |= (page << 8);
retval = synaptics_rmi4_reg_read(rmi4_data,
addr,
(unsigned char *)&fd,
sizeof(fd));
if (retval < 0)
return retval;
addr &= ~(MASK_8BIT << 8);
if (fd.fn_number) {
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Found F%02x\n",
__func__, fd.fn_number);
switch (fd.fn_number) {
case SYNAPTICS_RMI4_F12:
goto f12_found;
}
} else {
break;
}
intr_count += fd.intr_src_count;
}
}
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to find F12\n",
__func__);
return -EINVAL;
f12_found:
apen->query_base_addr = fd.query_base_addr | (page << 8);
apen->control_base_addr = fd.ctrl_base_addr | (page << 8);
apen->data_base_addr = fd.data_base_addr | (page << 8);
apen->command_base_addr = fd.cmd_base_addr | (page << 8);
retval = apen_reg_init();
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to initialize active pen registers\n",
__func__);
return retval;
}
apen->intr_mask = 0;
intr_src = fd.intr_src_count;
intr_off = intr_count % 8;
for (ii = intr_off;
ii < (intr_src + intr_off);
ii++) {
apen->intr_mask |= 1 << ii;
}
rmi4_data->intr_mask[0] |= apen->intr_mask;
addr = rmi4_data->f01_ctrl_base_addr + 1;
retval = synaptics_rmi4_reg_write(rmi4_data,
addr,
&(rmi4_data->intr_mask[0]),
sizeof(rmi4_data->intr_mask[0]));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to set interrupt enable bit\n",
__func__);
return retval;
}
return 0;
}
static void synaptics_rmi4_apen_attn(struct synaptics_rmi4_data *rmi4_data,
unsigned char intr_mask)
{
if (!apen)
return;
if (apen->intr_mask & intr_mask)
apen_report();
return;
}
static int synaptics_rmi4_apen_init(struct synaptics_rmi4_data *rmi4_data)
{
int retval;
if (apen) {
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Handle already exists\n",
__func__);
return 0;
}
apen = kzalloc(sizeof(*apen), GFP_KERNEL);
if (!apen) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for apen\n",
__func__);
retval = -ENOMEM;
goto exit;
}
apen->apen_data = kzalloc(sizeof(*(apen->apen_data)), GFP_KERNEL);
if (!apen->apen_data) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for apen_data\n",
__func__);
retval = -ENOMEM;
goto exit_free_apen;
}
apen->rmi4_data = rmi4_data;
retval = apen_scan_pdt();
if (retval < 0)
goto exit_free_apen_data;
apen->apen_dev = input_allocate_device();
if (apen->apen_dev == NULL) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to allocate active pen device\n",
__func__);
retval = -ENOMEM;
goto exit_free_apen_data;
}
apen->apen_dev->name = ACTIVE_PEN_DRIVER_NAME;
apen->apen_dev->phys = APEN_PHYS_NAME;
apen->apen_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT;
apen->apen_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION;
apen->apen_dev->dev.parent = rmi4_data->pdev->dev.parent;
input_set_drvdata(apen->apen_dev, rmi4_data);
set_bit(EV_KEY, apen->apen_dev->evbit);
set_bit(EV_ABS, apen->apen_dev->evbit);
set_bit(BTN_TOUCH, apen->apen_dev->keybit);
set_bit(BTN_TOOL_PEN, apen->apen_dev->keybit);
set_bit(BTN_TOOL_RUBBER, apen->apen_dev->keybit);
set_bit(BTN_STYLUS, apen->apen_dev->keybit);
#ifdef INPUT_PROP_DIRECT
set_bit(INPUT_PROP_DIRECT, apen->apen_dev->propbit);
#endif
apen_set_params();
retval = input_register_device(apen->apen_dev);
if (retval) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to register active pen device\n",
__func__);
goto exit_free_input_device;
}
return 0;
exit_free_input_device:
input_free_device(apen->apen_dev);
exit_free_apen_data:
kfree(apen->apen_data);
exit_free_apen:
kfree(apen);
apen = NULL;
exit:
return retval;
}
static void synaptics_rmi4_apen_remove(struct synaptics_rmi4_data *rmi4_data)
{
if (!apen)
goto exit;
input_unregister_device(apen->apen_dev);
kfree(apen->apen_data);
kfree(apen);
apen = NULL;
exit:
complete(&apen_remove_complete);
}
static void synaptics_rmi4_apen_reset(struct synaptics_rmi4_data *rmi4_data)
{
if (!apen) {
synaptics_rmi4_apen_init(rmi4_data);
return;
}
apen_lift();
apen_scan_pdt();
}
static void synaptics_rmi4_apen_reinit(struct synaptics_rmi4_data *rmi4_data)
{
if (!apen)
return;
apen_lift();
}
static void synaptics_rmi4_apen_e_suspend(struct synaptics_rmi4_data *rmi4_data)
{
if (!apen)
return;
apen_lift();
}
static void synaptics_rmi4_apen_suspend(struct synaptics_rmi4_data *rmi4_data)
{
if (!apen)
return;
apen_lift();
}
static struct synaptics_rmi4_exp_fn active_pen_module = {
.fn_type = RMI_ACTIVE_PEN,
.init = synaptics_rmi4_apen_init,
.remove = synaptics_rmi4_apen_remove,
.reset = synaptics_rmi4_apen_reset,
.reinit = synaptics_rmi4_apen_reinit,
.early_suspend = synaptics_rmi4_apen_e_suspend,
.suspend = synaptics_rmi4_apen_suspend,
.resume = NULL,
.late_resume = NULL,
.attn = synaptics_rmi4_apen_attn,
};
static int __init rmi4_active_pen_module_init(void)
{
synaptics_rmi4_new_function(&active_pen_module, true);
return 0;
}
static void __exit rmi4_active_pen_module_exit(void)
{
synaptics_rmi4_new_function(&active_pen_module, false);
wait_for_completion(&apen_remove_complete);
}
module_init(rmi4_active_pen_module_init);
module_exit(rmi4_active_pen_module_exit);
MODULE_AUTHOR("Synaptics, Inc.");
MODULE_DESCRIPTION("Synaptics DSX Active Pen Module");
MODULE_LICENSE("GPL v2");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,538 @@
/*
* Synaptics DSX touchscreen driver
*
* Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved.
*
* Copyright (c) 2018 The Linux Foundation. All rights reserved.
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
*
* 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.
*
* INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
* EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
* AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
* IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
* WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
* AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
* NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
* TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
* DOLLARS.
*/
#ifndef _SYNAPTICS_DSX_RMI4_H_
#define _SYNAPTICS_DSX_RMI4_H_
#define SYNAPTICS_DS4 (1 << 0)
#define SYNAPTICS_DS5 (1 << 1)
#define SYNAPTICS_DSX_DRIVER_PRODUCT (SYNAPTICS_DS4 | SYNAPTICS_DS5)
#define SYNAPTICS_DSX_DRIVER_VERSION 0x2070
#include <linux/version.h>
#ifdef CONFIG_FB
#include <linux/notifier.h>
#include <linux/fb.h>
#endif
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38))
#define KERNEL_ABOVE_2_6_38
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
#define KERNEL_ABOVE_3_6
#endif
#ifdef KERNEL_ABOVE_2_6_38
#define sstrtoul(...) kstrtoul(__VA_ARGS__)
#else
#define sstrtoul(...) strict_strtoul(__VA_ARGS__)
#endif
/*
*#define F51_DISCRETE_FORCE
*#ifdef F51_DISCRETE_FORCE
*#define FORCE_LEVEL_ADDR 0x0419
*#define FORCE_LEVEL_MAX 255
*#define CAL_DATA_SIZE 144
*#endif
*#define SYNA_TDDI
*/
#define PDT_PROPS (0X00EF)
#define PDT_START (0x00E9)
#define PDT_END (0x00D0)
#define PDT_ENTRY_SIZE (0x0006)
#define PAGES_TO_SERVICE (10)
#define PAGE_SELECT_LEN (2)
#define ADDRESS_LEN (2)
#define SYNAPTICS_RMI4_F01 (0x01)
#define SYNAPTICS_RMI4_F11 (0x11)
#define SYNAPTICS_RMI4_F12 (0x12)
#define SYNAPTICS_RMI4_F1A (0x1A)
#define SYNAPTICS_RMI4_F21 (0x21)
#define SYNAPTICS_RMI4_F34 (0x34)
#define SYNAPTICS_RMI4_F35 (0x35)
#define SYNAPTICS_RMI4_F38 (0x38)
#define SYNAPTICS_RMI4_F51 (0x51)
#define SYNAPTICS_RMI4_F54 (0x54)
#define SYNAPTICS_RMI4_F55 (0x55)
#define SYNAPTICS_RMI4_FDB (0xDB)
#define PRODUCT_INFO_SIZE 2
#define PRODUCT_ID_SIZE 10
#define BUILD_ID_SIZE 3
#define F12_FINGERS_TO_SUPPORT 10
#define F12_NO_OBJECT_STATUS 0x00
#define F12_FINGER_STATUS 0x01
#define F12_ACTIVE_STYLUS_STATUS 0x02
#define F12_PALM_STATUS 0x03
#define F12_HOVERING_FINGER_STATUS 0x05
#define F12_GLOVED_FINGER_STATUS 0x06
#define F12_NARROW_OBJECT_STATUS 0x07
#define F12_HAND_EDGE_STATUS 0x08
#define F12_COVER_STATUS 0x0A
#define F12_STYLUS_STATUS 0x0B
#define F12_ERASER_STATUS 0x0C
#define F12_SMALL_OBJECT_STATUS 0x0D
#define F12_GESTURE_DETECTION_LEN 5
#define MAX_NUMBER_OF_BUTTONS 4
#define MAX_INTR_REGISTERS 4
#define MASK_16BIT 0xFFFF
#define MASK_8BIT 0xFF
#define MASK_7BIT 0x7F
#define MASK_6BIT 0x3F
#define MASK_5BIT 0x1F
#define MASK_4BIT 0x0F
#define MASK_3BIT 0x07
#define MASK_2BIT 0x03
#define MASK_1BIT 0x01
#define PINCTRL_STATE_ACTIVE "pmx_ts_active"
#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend"
#define PINCTRL_STATE_RELEASE "pmx_ts_release"
enum exp_fn {
RMI_DEV = 0,
RMI_FW_UPDATER,
RMI_TEST_REPORTING,
RMI_PROXIMITY,
RMI_ACTIVE_PEN,
RMI_GESTURE,
RMI_VIDEO,
RMI_DEBUG,
RMI_LAST,
};
/*
* struct synaptics_rmi4_fn_desc - function descriptor fields in PDT entry
* @query_base_addr: base address for query registers
* @cmd_base_addr: base address for command registers
* @ctrl_base_addr: base address for control registers
* @data_base_addr: base address for data registers
* @intr_src_count: number of interrupt sources
* @fn_version: version of function
* @fn_number: function number
*/
struct synaptics_rmi4_fn_desc {
union {
struct {
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:3;
unsigned char reserved_1:2;
unsigned char fn_version:2;
unsigned char reserved_2:1;
unsigned char fn_number;
} __packed;
unsigned char data[6];
};
};
/*
* synaptics_rmi4_fn_full_addr - full 16-bit base addresses
* @query_base: 16-bit base address for query registers
* @cmd_base: 16-bit base address for command registers
* @ctrl_base: 16-bit base address for control registers
* @data_base: 16-bit base address for data registers
*/
struct synaptics_rmi4_fn_full_addr {
unsigned short query_base;
unsigned short cmd_base;
unsigned short ctrl_base;
unsigned short data_base;
};
/*
* struct synaptics_rmi4_f11_extra_data - extra data of F$11
* @data38_offset: offset to F11_2D_DATA38 register
*/
struct synaptics_rmi4_f11_extra_data {
unsigned char data38_offset;
};
/*
* struct synaptics_rmi4_f12_extra_data - extra data of F$12
* @data1_offset: offset to F12_2D_DATA01 register
* @data4_offset: offset to F12_2D_DATA04 register
* @data15_offset: offset to F12_2D_DATA15 register
* @data15_size: size of F12_2D_DATA15 register
* @data15_data: buffer for reading F12_2D_DATA15 register
* @data29_offset: offset to F12_2D_DATA29 register
* @data29_size: size of F12_2D_DATA29 register
* @data29_data: buffer for reading F12_2D_DATA29 register
* @ctrl20_offset: offset to F12_2D_CTRL20 register
*/
struct synaptics_rmi4_f12_extra_data {
unsigned char data1_offset;
unsigned char data4_offset;
unsigned char data15_offset;
unsigned char data15_size;
unsigned char data15_data[(F12_FINGERS_TO_SUPPORT + 7) / 8];
unsigned char data29_offset;
unsigned char data29_size;
unsigned char data29_data[F12_FINGERS_TO_SUPPORT * 2];
unsigned char ctrl20_offset;
};
/*
* struct synaptics_rmi4_fn - RMI function handler
* @fn_number: function number
* @num_of_data_sources: number of data sources
* @num_of_data_points: maximum number of fingers supported
* @intr_reg_num: index to associated interrupt register
* @intr_mask: interrupt mask
* @full_addr: full 16-bit base addresses of function registers
* @link: linked list for function handlers
* @data_size: size of private data
* @data: pointer to private data
* @extra: pointer to extra data
*/
struct synaptics_rmi4_fn {
unsigned char fn_number;
unsigned char num_of_data_sources;
unsigned char num_of_data_points;
unsigned char intr_reg_num;
unsigned char intr_mask;
struct synaptics_rmi4_fn_full_addr full_addr;
struct list_head link;
int data_size;
void *data;
void *extra;
};
/*
* struct synaptics_rmi4_input_settings - current input settings
* @num_of_fingers: maximum number of fingers for 2D touch
* @valid_button_count: number of valid 0D buttons
* @max_touch_width: maximum touch width
* @sensor_max_x: maximum x coordinate for 2D touch
* @sensor_max_y: maximum y coordinate for 2D touch
* @force_min: minimum force value
* @force_max: maximum force value
* @stylus_enable: flag to indicate reporting of stylus data
* @eraser_enable: flag to indicate reporting of eraser data
*/
struct synaptics_rmi4_input_settings {
unsigned char num_of_fingers;
unsigned char valid_button_count;
unsigned char max_touch_width;
int sensor_max_x;
int sensor_max_y;
int force_min;
int force_max;
bool stylus_enable;
bool eraser_enable;
};
/*
* struct synaptics_rmi4_device_info - device information
* @version_major: RMI protocol major version number
* @version_minor: RMI protocol minor version number
* @manufacturer_id: manufacturer ID
* @product_props: product properties
* @product_info: product information
* @product_id_string: product ID
* @build_id: firmware build ID
* @support_fn_list: linked list for function handlers
*/
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[PRODUCT_INFO_SIZE];
unsigned char product_id_string[PRODUCT_ID_SIZE + 1];
unsigned char build_id[BUILD_ID_SIZE];
struct list_head support_fn_list;
};
/*
* struct synaptics_rmi4_data - RMI4 device instance data
* @pdev: pointer to platform device
* @input_dev: pointer to associated input device
* @stylus_dev: pointer to associated stylus device
* @hw_if: pointer to hardware interface data
* @rmi4_mod_info: device information
* @board_prop_dir: /sys/board_properties directory for virtual key map file
* @pwr_reg: pointer to regulator for power control
* @bus_reg: pointer to regulator for bus pullup control
* @rmi4_reset_mutex: mutex for software reset
* @rmi4_report_mutex: mutex for input event reporting
* @rmi4_io_ctrl_mutex: mutex for communication interface I/O
* @rmi4_exp_init_mutex: mutex for expansion function module initialization
* @rmi4_irq_enable_mutex: mutex for enabling/disabling interrupt
* @rb_work: work for rebuilding input device
* @rb_workqueue: workqueue for rebuilding input device
* @fb_notifier: framebuffer notifier client
* @reset_work: work for issuing reset after display framebuffer ready
* @reset_workqueue: workqueue for issuing reset after display framebuffer ready
* @early_suspend: early suspend power management
* @current_page: current RMI page for register access
* @button_0d_enabled: switch for enabling 0d button support
* @num_of_tx: number of Tx channels for 2D touch
* @num_of_rx: number of Rx channels for 2D touch
* @num_of_fingers: maximum number of fingers for 2D touch
* @max_touch_width: maximum touch width
* @valid_button_count: number of valid 0D buttons
* @report_enable: input data to report for F$12
* @no_sleep_setting: default setting of NoSleep in F01_RMI_CTRL00 register
* @gesture_detection: detected gesture type and properties
* @intr_mask: interrupt enable mask
* @button_txrx_mapping: Tx Rx mapping of 0D buttons
* @num_of_intr_regs: number of interrupt registers
* @f01_query_base_addr: query base address for f$01
* @f01_cmd_base_addr: command base address for f$01
* @f01_ctrl_base_addr: control base address for f$01
* @f01_data_base_addr: data base address for f$01
* @f51_query_base_addr: query base address for f$51
* @firmware_id: firmware build ID
* @irq: attention interrupt
* @sensor_max_x: maximum x coordinate for 2D touch
* @sensor_max_y: maximum y coordinate for 2D touch
* @force_min: minimum force value
* @force_max: maximum force value
* @set_wakeup_gesture: location of set wakeup gesture
* @flash_prog_mode: flag to indicate flash programming mode status
* @irq_enabled: flag to indicate attention interrupt enable status
* @fingers_on_2d: flag to indicate presence of fingers in 2D area
* @suspend: flag to indicate whether in suspend state
* @sensor_sleep: flag to indicate sleep state of sensor
* @stay_awake: flag to indicate whether to stay awake during suspend
* @fb_ready: flag to indicate whether display framebuffer in ready state
* @f11_wakeup_gesture: flag to indicate support for wakeup gestures in F$11
* @f12_wakeup_gesture: flag to indicate support for wakeup gestures in F$12
* @enable_wakeup_gesture: flag to indicate usage of wakeup gestures
* @wedge_sensor: flag to indicate use of wedge sensor
* @report_pressure: flag to indicate reporting of pressure data
* @stylus_enable: flag to indicate reporting of stylus data
* @eraser_enable: flag to indicate reporting of eraser data
* @external_afe_buttons: flag to indicate presence of external AFE buttons
* @reset_device: pointer to device reset function
* @irq_enable: pointer to interrupt enable function
* @sleep_enable: pointer to sleep enable function
* @report_touch: pointer to touch reporting function
*/
struct synaptics_rmi4_data {
bool initialized;
struct platform_device *pdev;
struct input_dev *input_dev;
struct input_dev *stylus_dev;
const struct synaptics_dsx_hw_interface *hw_if;
struct synaptics_rmi4_device_info rmi4_mod_info;
struct synaptics_rmi4_input_settings input_settings;
struct kobject *board_prop_dir;
struct regulator *pwr_reg;
struct regulator *bus_reg;
struct mutex rmi4_reset_mutex;
struct mutex rmi4_report_mutex;
struct mutex rmi4_io_ctrl_mutex;
struct mutex rmi4_exp_init_mutex;
struct mutex rmi4_irq_enable_mutex;
struct delayed_work rb_work;
struct workqueue_struct *rb_workqueue;
struct work_struct rmi4_probe_work;
struct workqueue_struct *rmi4_probe_wq;
struct completion drm_init_done;
struct pinctrl *ts_pinctrl;
struct pinctrl_state *pinctrl_state_active;
struct pinctrl_state *pinctrl_state_suspend;
struct pinctrl_state *pinctrl_state_release;
struct notifier_block fb_notifier;
struct work_struct reset_work;
struct workqueue_struct *reset_workqueue;
#ifdef CONFIG_HAS_EARLYSUSPEND
struct early_suspend early_suspend;
#endif
unsigned char current_page;
unsigned char button_0d_enabled;
unsigned char num_of_tx;
unsigned char num_of_rx;
unsigned char num_of_fingers;
unsigned char max_touch_width;
unsigned char valid_button_count;
unsigned char report_enable;
unsigned char no_sleep_setting;
unsigned char gesture_detection[F12_GESTURE_DETECTION_LEN];
unsigned char intr_mask[MAX_INTR_REGISTERS];
unsigned char *button_txrx_mapping;
unsigned short num_of_intr_regs;
unsigned short f01_query_base_addr;
unsigned short f01_cmd_base_addr;
unsigned short f01_ctrl_base_addr;
unsigned short f01_data_base_addr;
#ifdef F51_DISCRETE_FORCE
unsigned short f51_query_base_addr;
#endif
unsigned int firmware_id;
int irq;
int sensor_max_x;
int sensor_max_y;
int force_min;
int force_max;
int set_wakeup_gesture;
int avdd_status;
int vdd_status;
bool flash_prog_mode;
bool irq_enabled;
bool fingers_on_2d;
bool suspend;
bool sensor_sleep;
bool stay_awake;
bool fb_ready;
bool f11_wakeup_gesture;
bool f12_wakeup_gesture;
bool enable_wakeup_gesture;
bool wedge_sensor;
bool report_pressure;
bool stylus_enable;
bool eraser_enable;
bool external_afe_buttons;
int (*reset_device)(struct synaptics_rmi4_data *rmi4_data,
bool rebuild);
int (*irq_enable)(struct synaptics_rmi4_data *rmi4_data, bool enable,
bool attn_only);
int (*sleep_enable)(struct synaptics_rmi4_data *rmi4_data,
bool enable);
void (*report_touch)(struct synaptics_rmi4_data *rmi4_data,
struct synaptics_rmi4_fn *fhandler);
};
struct synaptics_dsx_bus_access {
unsigned char type;
int (*read)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr,
unsigned char *data, unsigned int length);
int (*write)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr,
unsigned char *data, unsigned int length);
};
struct synaptics_dsx_hw_interface {
struct synaptics_dsx_board_data *board_data;
const struct synaptics_dsx_bus_access *bus_access;
int (*bl_hw_init)(struct synaptics_rmi4_data *rmi4_data);
int (*ui_hw_init)(struct synaptics_rmi4_data *rmi4_data);
};
struct synaptics_rmi4_exp_fn {
enum exp_fn fn_type;
int (*init)(struct synaptics_rmi4_data *rmi4_data);
void (*remove)(struct synaptics_rmi4_data *rmi4_data);
void (*reset)(struct synaptics_rmi4_data *rmi4_data);
void (*reinit)(struct synaptics_rmi4_data *rmi4_data);
void (*early_suspend)(struct synaptics_rmi4_data *rmi4_data);
void (*suspend)(struct synaptics_rmi4_data *rmi4_data);
void (*resume)(struct synaptics_rmi4_data *rmi4_data);
void (*late_resume)(struct synaptics_rmi4_data *rmi4_data);
void (*attn)(struct synaptics_rmi4_data *rmi4_data,
unsigned char intr_mask);
};
int synaptics_rmi4_bus_init(void);
void synaptics_rmi4_bus_exit(void);
void synaptics_rmi4_new_function(struct synaptics_rmi4_exp_fn *exp_fn_module,
bool insert);
int synaptics_fw_updater(const unsigned char *fw_data);
static inline int synaptics_rmi4_reg_read(
struct synaptics_rmi4_data *rmi4_data,
unsigned short addr,
unsigned char *data,
unsigned int len)
{
return rmi4_data->hw_if->bus_access->read(rmi4_data, addr, data, len);
}
static inline int synaptics_rmi4_reg_write(
struct synaptics_rmi4_data *rmi4_data,
unsigned short addr,
unsigned char *data,
unsigned int len)
{
return rmi4_data->hw_if->bus_access->write(rmi4_data, addr, data, len);
}
static inline ssize_t synaptics_rmi4_show_error(struct device *dev,
struct device_attribute *attr, char *buf)
{
dev_warn(dev, "%s Attempted to read from write-only attribute %s\n",
__func__, attr->attr.name);
return -EPERM;
}
static inline ssize_t synaptics_rmi4_store_error(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
dev_warn(dev, "%s Attempted to write to read-only attribute %s\n",
__func__, attr->attr.name);
return -EPERM;
}
static inline int secure_memcpy(unsigned char *dest, unsigned int dest_size,
const unsigned char *src, unsigned int src_size,
unsigned int count)
{
if (dest == NULL || src == NULL)
return -EINVAL;
if (count > dest_size || count > src_size)
return -EINVAL;
memcpy((void *)dest, (const void *)src, count);
return 0;
}
static inline void batohs(unsigned short *dest, unsigned char *src)
{
*dest = src[1] * 0x100 + src[0];
}
static inline void hstoba(unsigned char *dest, unsigned short src)
{
dest[0] = src % 0x100;
dest[1] = src / 0x100;
}
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,609 @@
/*
* Synaptics DSX touchscreen driver
*
* Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved.
*
* Copyright (c) 2018 The Linux Foundation. All rights reserved.
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
*
* 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.
*
* INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
* EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
* AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
* IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
* WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
* AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
* NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
* TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
* DOLLARS.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/types.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/input/synaptics_dsx.h>
#include "synaptics_dsx_core.h"
#include "linux/moduleparam.h"
#define SYN_I2C_RETRY_TIMES 10
#define rd_msgs 1
static unsigned char *wr_buf;
static struct synaptics_dsx_hw_interface hw_if;
static struct platform_device *synaptics_dsx_i2c_device;
#ifdef CONFIG_OF
static int parse_dt(struct device *dev, struct synaptics_dsx_board_data *bdata)
{
int retval;
u32 value;
const char *name;
struct property *prop;
struct device_node *np = dev->of_node;
bdata->irq_gpio = of_get_named_gpio_flags(np,
"synaptics,irq-gpio", 0,
(enum of_gpio_flags *)&bdata->irq_flags);
retval = of_property_read_u32(np, "synaptics,irq-on-state",
&value);
if (retval < 0)
bdata->irq_on_state = 0;
else
bdata->irq_on_state = value;
retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name);
if (retval < 0)
bdata->pwr_reg_name = NULL;
else
bdata->pwr_reg_name = name;
retval = of_property_read_string(np, "synaptics,bus-reg-name", &name);
if (retval < 0)
bdata->bus_reg_name = NULL;
else
bdata->bus_reg_name = name;
prop = of_find_property(np, "synaptics,power-gpio", NULL);
if (prop && prop->length) {
bdata->power_gpio = of_get_named_gpio_flags(np,
"synaptics,power-gpio", 0, NULL);
retval = of_property_read_u32(np, "synaptics,power-on-state",
&value);
if (retval < 0) {
dev_err(dev, "%s: Unable to read synaptics,power-on-state property\n",
__func__);
return retval;
} else {
bdata->power_on_state = value;
}
} else {
bdata->power_gpio = -1;
}
prop = of_find_property(np, "synaptics,power-delay-ms", NULL);
if (prop && prop->length) {
retval = of_property_read_u32(np, "synaptics,power-delay-ms",
&value);
if (retval < 0) {
dev_err(dev, "%s: Unable to read synaptics,power-delay-ms property\n",
__func__);
return retval;
} else {
bdata->power_delay_ms = value;
}
} else {
bdata->power_delay_ms = 0;
}
prop = of_find_property(np, "synaptics,reset-gpio", NULL);
if (prop && prop->length) {
bdata->reset_gpio = of_get_named_gpio_flags(np,
"synaptics,reset-gpio", 0, NULL);
retval = of_property_read_u32(np, "synaptics,reset-on-state",
&value);
if (retval < 0) {
dev_err(dev, "%s: Unable to read synaptics,reset-on-state property\n",
__func__);
return retval;
} else {
bdata->reset_on_state = value;
}
retval = of_property_read_u32(np, "synaptics,reset-active-ms",
&value);
if (retval < 0) {
dev_err(dev, "%s: Unable to read synaptics,reset-active-ms property\n",
__func__);
return retval;
} else {
bdata->reset_active_ms = value;
}
} else {
bdata->reset_gpio = -1;
}
prop = of_find_property(np, "synaptics,reset-delay-ms", NULL);
if (prop && prop->length) {
retval = of_property_read_u32(np, "synaptics,reset-delay-ms",
&value);
if (retval < 0) {
dev_err(dev, "%s: Unable to read synaptics,reset-delay-ms property\n",
__func__);
return retval;
} else {
bdata->reset_delay_ms = value;
}
} else {
bdata->reset_delay_ms = 0;
}
prop = of_find_property(np, "synaptics,max-y-for-2d", NULL);
if (prop && prop->length) {
retval = of_property_read_u32(np, "synaptics,max-y-for-2d",
&value);
if (retval < 0) {
dev_err(dev, "%s: Unable to read synaptics,max-y-for-2d property\n",
__func__);
return retval;
} else {
bdata->max_y_for_2d = value;
}
} else {
bdata->max_y_for_2d = -1;
}
bdata->swap_axes = of_property_read_bool(np, "synaptics,swap-axes");
bdata->x_flip = of_property_read_bool(np, "synaptics,x-flip");
bdata->y_flip = of_property_read_bool(np, "synaptics,y-flip");
prop = of_find_property(np, "synaptics,ub-i2c-addr", NULL);
if (prop && prop->length) {
retval = of_property_read_u32(np, "synaptics,ub-i2c-addr",
&value);
if (retval < 0) {
dev_err(dev, "%s: Unable to read synaptics,ub-i2c-addr property\n",
__func__);
return retval;
} else {
bdata->ub_i2c_addr = (unsigned short)value;
}
} else {
bdata->ub_i2c_addr = -1;
}
prop = of_find_property(np, "synaptics,cap-button-codes", NULL);
if (prop && prop->length) {
bdata->cap_button_map->map = devm_kzalloc(dev,
prop->length,
GFP_KERNEL);
if (!bdata->cap_button_map->map)
return -ENOMEM;
bdata->cap_button_map->nbuttons = prop->length / sizeof(u32);
retval = of_property_read_u32_array(np,
"synaptics,cap-button-codes",
bdata->cap_button_map->map,
bdata->cap_button_map->nbuttons);
if (retval < 0) {
bdata->cap_button_map->nbuttons = 0;
bdata->cap_button_map->map = NULL;
}
} else {
bdata->cap_button_map->nbuttons = 0;
bdata->cap_button_map->map = NULL;
}
prop = of_find_property(np, "synaptics,vir-button-codes", NULL);
if (prop && prop->length) {
bdata->vir_button_map->map = devm_kzalloc(dev,
prop->length,
GFP_KERNEL);
if (!bdata->vir_button_map->map)
return -ENOMEM;
bdata->vir_button_map->nbuttons = prop->length / sizeof(u32);
bdata->vir_button_map->nbuttons /= 5;
retval = of_property_read_u32_array(np,
"synaptics,vir-button-codes",
bdata->vir_button_map->map,
bdata->vir_button_map->nbuttons * 5);
if (retval < 0) {
bdata->vir_button_map->nbuttons = 0;
bdata->vir_button_map->map = NULL;
}
} else {
bdata->vir_button_map->nbuttons = 0;
bdata->vir_button_map->map = NULL;
}
return 0;
}
#endif
static int synaptics_rmi4_i2c_alloc_buf(struct synaptics_rmi4_data *rmi4_data,
unsigned int count)
{
static unsigned int buf_size;
if (count > buf_size) {
if (buf_size)
kfree(wr_buf);
wr_buf = kzalloc(count, GFP_KERNEL);
if (!wr_buf) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for buffer\n",
__func__);
buf_size = 0;
return -ENOMEM;
}
buf_size = count;
}
return 0;
}
static void synaptics_rmi4_i2c_check_addr(struct synaptics_rmi4_data *rmi4_data,
struct i2c_client *i2c)
{
if (hw_if.board_data->ub_i2c_addr == -1)
return;
if (hw_if.board_data->i2c_addr == i2c->addr)
hw_if.board_data->i2c_addr = hw_if.board_data->ub_i2c_addr;
else
hw_if.board_data->i2c_addr = i2c->addr;
return;
}
static int synaptics_rmi4_i2c_set_page(struct synaptics_rmi4_data *rmi4_data,
unsigned short addr)
{
int retval = 0;
unsigned char retry;
unsigned char buf[PAGE_SELECT_LEN];
unsigned char page;
struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
struct i2c_msg msg[2];
msg[0].addr = hw_if.board_data->i2c_addr;
msg[0].flags = 0;
msg[0].len = PAGE_SELECT_LEN;
msg[0].buf = buf;
page = ((addr >> 8) & MASK_8BIT);
buf[0] = MASK_8BIT;
buf[1] = page;
if (page != rmi4_data->current_page) {
for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
if (i2c_transfer(i2c->adapter, &msg[0], 1) == 1) {
rmi4_data->current_page = page;
retval = PAGE_SELECT_LEN;
break;
}
dev_err(rmi4_data->pdev->dev.parent,
"%s: I2C retry %d\n",
__func__, retry + 1);
msleep(20);
if (retry == SYN_I2C_RETRY_TIMES / 2) {
synaptics_rmi4_i2c_check_addr(rmi4_data, i2c);
msg[0].addr = hw_if.board_data->i2c_addr;
}
}
} else {
retval = PAGE_SELECT_LEN;
}
return retval;
}
static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data,
unsigned short addr, unsigned char *data, unsigned int length)
{
int retval = 0;
unsigned char retry;
unsigned char buf;
unsigned char index = 0;
unsigned char xfer_msgs;
unsigned char remaining_msgs;
unsigned short i2c_addr;
unsigned short data_offset = 0;
unsigned int remaining_length = length;
struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
struct i2c_adapter *adap = i2c->adapter;
struct i2c_msg msg[rd_msgs + 1];
mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
retval = synaptics_rmi4_i2c_set_page(rmi4_data, addr);
if (retval != PAGE_SELECT_LEN) {
retval = -EIO;
goto exit;
}
msg[0].addr = hw_if.board_data->i2c_addr;
msg[0].flags = 0;
msg[0].len = 1;
msg[0].buf = &buf;
msg[rd_msgs].addr = hw_if.board_data->i2c_addr;
msg[rd_msgs].flags = I2C_M_RD;
msg[rd_msgs].len = (unsigned short)remaining_length;
msg[rd_msgs].buf = &data[data_offset];
buf = addr & MASK_8BIT;
remaining_msgs = rd_msgs + 1;
while (remaining_msgs) {
xfer_msgs = remaining_msgs;
for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
retval = i2c_transfer(adap, &msg[index], xfer_msgs);
if (retval == xfer_msgs)
break;
dev_err(rmi4_data->pdev->dev.parent,
"%s: I2C retry %d\n",
__func__, retry + 1);
msleep(20);
if (retry == SYN_I2C_RETRY_TIMES / 2) {
synaptics_rmi4_i2c_check_addr(rmi4_data, i2c);
i2c_addr = hw_if.board_data->i2c_addr;
msg[0].addr = i2c_addr;
msg[rd_msgs].addr = i2c_addr;
}
}
if (retry == SYN_I2C_RETRY_TIMES) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: I2C read over retry limit\n",
__func__);
retval = -EIO;
goto exit;
}
remaining_msgs -= xfer_msgs;
index += xfer_msgs;
}
retval = length;
exit:
mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
return retval;
}
static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data,
unsigned short addr, unsigned char *data, unsigned int length)
{
int retval;
unsigned char retry;
struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
struct i2c_msg msg[2];
mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
retval = synaptics_rmi4_i2c_alloc_buf(rmi4_data, length + 1);
if (retval < 0)
goto exit;
retval = synaptics_rmi4_i2c_set_page(rmi4_data, addr);
if (retval != PAGE_SELECT_LEN) {
retval = -EIO;
goto exit;
}
msg[0].addr = hw_if.board_data->i2c_addr;
msg[0].flags = 0;
msg[0].len = (unsigned short)(length + 1);
msg[0].buf = wr_buf;
wr_buf[0] = addr & MASK_8BIT;
retval = secure_memcpy(&wr_buf[1], length, &data[0], length, length);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to copy data\n",
__func__);
goto exit;
}
for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
if (i2c_transfer(i2c->adapter, &msg[0], 1) == 1) {
retval = length;
break;
}
dev_err(rmi4_data->pdev->dev.parent,
"%s: I2C retry %d\n",
__func__, retry + 1);
msleep(20);
if (retry == SYN_I2C_RETRY_TIMES / 2) {
synaptics_rmi4_i2c_check_addr(rmi4_data, i2c);
msg[0].addr = hw_if.board_data->i2c_addr;
}
}
if (retry == SYN_I2C_RETRY_TIMES) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: I2C write over retry limit\n",
__func__);
retval = -EIO;
}
exit:
mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
return retval;
}
static struct synaptics_dsx_bus_access bus_access = {
.type = BUS_I2C,
.read = synaptics_rmi4_i2c_read,
.write = synaptics_rmi4_i2c_write,
};
static void synaptics_rmi4_i2c_dev_release(struct device *dev)
{
kfree(synaptics_dsx_i2c_device);
return;
}
static int synaptics_rmi4_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
int retval;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&client->dev,
"%s: SMBus byte data commands not supported by host\n",
__func__);
return -EIO;
}
synaptics_dsx_i2c_device = kzalloc(
sizeof(struct platform_device),
GFP_KERNEL);
if (!synaptics_dsx_i2c_device) {
dev_err(&client->dev,
"%s: Failed to allocate memory for synaptics_dsx_i2c_device\n",
__func__);
return -ENOMEM;
}
#ifdef CONFIG_OF
if (client->dev.of_node) {
hw_if.board_data = devm_kzalloc(&client->dev,
sizeof(struct synaptics_dsx_board_data),
GFP_KERNEL);
if (!hw_if.board_data) {
dev_err(&client->dev,
"%s: Failed to allocate memory for board data\n",
__func__);
return -ENOMEM;
}
hw_if.board_data->cap_button_map = devm_kzalloc(&client->dev,
sizeof(struct synaptics_dsx_button_map),
GFP_KERNEL);
if (!hw_if.board_data->cap_button_map) {
dev_err(&client->dev,
"%s: Failed to allocate memory for 0D button map\n",
__func__);
return -ENOMEM;
}
hw_if.board_data->vir_button_map = devm_kzalloc(&client->dev,
sizeof(struct synaptics_dsx_button_map),
GFP_KERNEL);
if (!hw_if.board_data->vir_button_map) {
dev_err(&client->dev,
"%s: Failed to allocate memory for virtual button map\n",
__func__);
return -ENOMEM;
}
parse_dt(&client->dev, hw_if.board_data);
}
#else
hw_if.board_data = client->dev.platform_data;
#endif
hw_if.bus_access = &bus_access;
hw_if.board_data->i2c_addr = client->addr;
synaptics_dsx_i2c_device->name = PLATFORM_DRIVER_NAME;
synaptics_dsx_i2c_device->id = 0;
synaptics_dsx_i2c_device->num_resources = 0;
synaptics_dsx_i2c_device->dev.parent = &client->dev;
synaptics_dsx_i2c_device->dev.platform_data = &hw_if;
synaptics_dsx_i2c_device->dev.release = synaptics_rmi4_i2c_dev_release;
retval = platform_device_register(synaptics_dsx_i2c_device);
if (retval) {
dev_err(&client->dev,
"%s: Failed to register platform device\n",
__func__);
return -ENODEV;
}
return 0;
}
static int synaptics_rmi4_i2c_remove(struct i2c_client *client)
{
platform_device_unregister(synaptics_dsx_i2c_device);
return 0;
}
static const struct i2c_device_id synaptics_rmi4_id_table[] = {
{I2C_DRIVER_NAME, 0},
{},
};
MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table);
#ifdef CONFIG_OF
static struct of_device_id synaptics_rmi4_of_match_table[] = {
{
.compatible = "synaptics,dsx-i2c",
},
{},
};
MODULE_DEVICE_TABLE(of, synaptics_rmi4_of_match_table);
#else
#define synaptics_rmi4_of_match_table NULL
#endif
static struct i2c_driver synaptics_rmi4_i2c_driver = {
.driver = {
.name = I2C_DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = synaptics_rmi4_of_match_table,
},
.probe = synaptics_rmi4_i2c_probe,
.remove = synaptics_rmi4_i2c_remove,
.id_table = synaptics_rmi4_id_table,
};
int synaptics_rmi4_bus_init(void)
{
return i2c_add_driver(&synaptics_rmi4_i2c_driver);
}
EXPORT_SYMBOL(synaptics_rmi4_bus_init);
void synaptics_rmi4_bus_exit(void)
{
kfree(wr_buf);
i2c_del_driver(&synaptics_rmi4_i2c_driver);
return;
}
EXPORT_SYMBOL(synaptics_rmi4_bus_exit);
MODULE_AUTHOR("Synaptics, Inc.");
MODULE_DESCRIPTION("Synaptics DSX I2C Bus Support Module");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,693 @@
/*
* Synaptics DSX touchscreen driver
*
* Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved.
*
* Copyright (c) 2018 The Linux Foundation. All rights reserved.
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
*
* 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.
*
* INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
* EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
* AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
* IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
* WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
* AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
* NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
* TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
* DOLLARS.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/input/synaptics_dsx.h>
#include "synaptics_dsx_core.h"
#define PROX_PHYS_NAME "synaptics_dsx/proximity"
#define HOVER_Z_MAX (255)
#define HOVERING_FINGER_EN (1 << 4)
static ssize_t synaptics_rmi4_hover_finger_en_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t synaptics_rmi4_hover_finger_en_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static struct device_attribute attrs[] = {
__ATTR(hover_finger_en, 0664,
synaptics_rmi4_hover_finger_en_show,
synaptics_rmi4_hover_finger_en_store),
};
struct synaptics_rmi4_f12_query_5 {
union {
struct {
unsigned char size_of_query6;
struct {
unsigned char ctrl0_is_present:1;
unsigned char ctrl1_is_present:1;
unsigned char ctrl2_is_present:1;
unsigned char ctrl3_is_present:1;
unsigned char ctrl4_is_present:1;
unsigned char ctrl5_is_present:1;
unsigned char ctrl6_is_present:1;
unsigned char ctrl7_is_present:1;
} __packed;
struct {
unsigned char ctrl8_is_present:1;
unsigned char ctrl9_is_present:1;
unsigned char ctrl10_is_present:1;
unsigned char ctrl11_is_present:1;
unsigned char ctrl12_is_present:1;
unsigned char ctrl13_is_present:1;
unsigned char ctrl14_is_present:1;
unsigned char ctrl15_is_present:1;
} __packed;
struct {
unsigned char ctrl16_is_present:1;
unsigned char ctrl17_is_present:1;
unsigned char ctrl18_is_present:1;
unsigned char ctrl19_is_present:1;
unsigned char ctrl20_is_present:1;
unsigned char ctrl21_is_present:1;
unsigned char ctrl22_is_present:1;
unsigned char ctrl23_is_present:1;
} __packed;
};
unsigned char data[4];
};
};
struct synaptics_rmi4_f12_query_8 {
union {
struct {
unsigned char size_of_query9;
struct {
unsigned char data0_is_present:1;
unsigned char data1_is_present:1;
unsigned char data2_is_present:1;
unsigned char data3_is_present:1;
unsigned char data4_is_present:1;
unsigned char data5_is_present:1;
unsigned char data6_is_present:1;
unsigned char data7_is_present:1;
} __packed;
};
unsigned char data[2];
};
};
struct prox_finger_data {
union {
struct {
unsigned char object_type_and_status;
unsigned char x_lsb;
unsigned char x_msb;
unsigned char y_lsb;
unsigned char y_msb;
unsigned char z;
} __packed;
unsigned char proximity_data[6];
};
};
struct synaptics_rmi4_prox_handle {
bool hover_finger_present;
bool hover_finger_en;
unsigned char intr_mask;
unsigned short query_base_addr;
unsigned short control_base_addr;
unsigned short data_base_addr;
unsigned short command_base_addr;
unsigned short hover_finger_en_addr;
unsigned short hover_finger_data_addr;
struct input_dev *prox_dev;
struct prox_finger_data *finger_data;
struct synaptics_rmi4_data *rmi4_data;
};
static struct synaptics_rmi4_prox_handle *prox;
DECLARE_COMPLETION(prox_remove_complete);
static void prox_hover_finger_lift(void)
{
input_report_key(prox->prox_dev, BTN_TOUCH, 0);
input_report_key(prox->prox_dev, BTN_TOOL_FINGER, 0);
input_sync(prox->prox_dev);
prox->hover_finger_present = false;
return;
}
static void prox_hover_finger_report(void)
{
int retval;
int x;
int y;
int z;
struct prox_finger_data *data;
struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
data = prox->finger_data;
retval = synaptics_rmi4_reg_read(rmi4_data,
prox->hover_finger_data_addr,
data->proximity_data,
sizeof(data->proximity_data));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read hovering finger data\n",
__func__);
return;
}
if (data->object_type_and_status != F12_HOVERING_FINGER_STATUS) {
if (prox->hover_finger_present)
prox_hover_finger_lift();
return;
}
x = (data->x_msb << 8) | (data->x_lsb);
y = (data->y_msb << 8) | (data->y_lsb);
z = HOVER_Z_MAX - data->z;
input_report_key(prox->prox_dev, BTN_TOUCH, 0);
input_report_key(prox->prox_dev, BTN_TOOL_FINGER, 1);
input_report_abs(prox->prox_dev, ABS_X, x);
input_report_abs(prox->prox_dev, ABS_Y, y);
input_report_abs(prox->prox_dev, ABS_DISTANCE, z);
input_sync(prox->prox_dev);
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: x = %d y = %d z = %d\n",
__func__, x, y, z);
prox->hover_finger_present = true;
return;
}
static int prox_set_hover_finger_en(void)
{
int retval;
unsigned char object_report_enable;
struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
retval = synaptics_rmi4_reg_read(rmi4_data,
prox->hover_finger_en_addr,
&object_report_enable,
sizeof(object_report_enable));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read from object report enable register\n",
__func__);
return retval;
}
if (prox->hover_finger_en)
object_report_enable |= HOVERING_FINGER_EN;
else
object_report_enable &= ~HOVERING_FINGER_EN;
retval = synaptics_rmi4_reg_write(rmi4_data,
prox->hover_finger_en_addr,
&object_report_enable,
sizeof(object_report_enable));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write to object report enable register\n",
__func__);
return retval;
}
return 0;
}
static void prox_set_params(void)
{
input_set_abs_params(prox->prox_dev, ABS_X, 0,
prox->rmi4_data->sensor_max_x, 0, 0);
input_set_abs_params(prox->prox_dev, ABS_Y, 0,
prox->rmi4_data->sensor_max_y, 0, 0);
input_set_abs_params(prox->prox_dev, ABS_DISTANCE, 0,
HOVER_Z_MAX, 0, 0);
return;
}
static int prox_reg_init(void)
{
int retval;
unsigned char ctrl_23_offset;
unsigned char data_1_offset;
struct synaptics_rmi4_f12_query_5 query_5;
struct synaptics_rmi4_f12_query_8 query_8;
struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
retval = synaptics_rmi4_reg_read(rmi4_data,
prox->query_base_addr + 5,
query_5.data,
sizeof(query_5.data));
if (retval < 0)
return retval;
ctrl_23_offset = query_5.ctrl0_is_present +
query_5.ctrl1_is_present +
query_5.ctrl2_is_present +
query_5.ctrl3_is_present +
query_5.ctrl4_is_present +
query_5.ctrl5_is_present +
query_5.ctrl6_is_present +
query_5.ctrl7_is_present +
query_5.ctrl8_is_present +
query_5.ctrl9_is_present +
query_5.ctrl10_is_present +
query_5.ctrl11_is_present +
query_5.ctrl12_is_present +
query_5.ctrl13_is_present +
query_5.ctrl14_is_present +
query_5.ctrl15_is_present +
query_5.ctrl16_is_present +
query_5.ctrl17_is_present +
query_5.ctrl18_is_present +
query_5.ctrl19_is_present +
query_5.ctrl20_is_present +
query_5.ctrl21_is_present +
query_5.ctrl22_is_present;
prox->hover_finger_en_addr = prox->control_base_addr + ctrl_23_offset;
retval = synaptics_rmi4_reg_read(rmi4_data,
prox->query_base_addr + 8,
query_8.data,
sizeof(query_8.data));
if (retval < 0)
return retval;
data_1_offset = query_8.data0_is_present;
prox->hover_finger_data_addr = prox->data_base_addr + data_1_offset;
return retval;
}
static int prox_scan_pdt(void)
{
int retval;
unsigned char ii;
unsigned char page;
unsigned char intr_count = 0;
unsigned char intr_off;
unsigned char intr_src;
unsigned short addr;
struct synaptics_rmi4_fn_desc fd;
struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
for (page = 0; page < PAGES_TO_SERVICE; page++) {
for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) {
addr |= (page << 8);
retval = synaptics_rmi4_reg_read(rmi4_data,
addr,
(unsigned char *)&fd,
sizeof(fd));
if (retval < 0)
return retval;
addr &= ~(MASK_8BIT << 8);
if (fd.fn_number) {
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Found F%02x\n",
__func__, fd.fn_number);
switch (fd.fn_number) {
case SYNAPTICS_RMI4_F12:
goto f12_found;
break;
}
} else {
break;
}
intr_count += fd.intr_src_count;
}
}
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to find F12\n",
__func__);
return -EINVAL;
f12_found:
prox->query_base_addr = fd.query_base_addr | (page << 8);
prox->control_base_addr = fd.ctrl_base_addr | (page << 8);
prox->data_base_addr = fd.data_base_addr | (page << 8);
prox->command_base_addr = fd.cmd_base_addr | (page << 8);
retval = prox_reg_init();
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to initialize proximity registers\n",
__func__);
return retval;
}
prox->intr_mask = 0;
intr_src = fd.intr_src_count;
intr_off = intr_count % 8;
for (ii = intr_off;
ii < (intr_src + intr_off);
ii++) {
prox->intr_mask |= 1 << ii;
}
rmi4_data->intr_mask[0] |= prox->intr_mask;
addr = rmi4_data->f01_ctrl_base_addr + 1;
retval = synaptics_rmi4_reg_write(rmi4_data,
addr,
&(rmi4_data->intr_mask[0]),
sizeof(rmi4_data->intr_mask[0]));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to set interrupt enable bit\n",
__func__);
return retval;
}
return 0;
}
static ssize_t synaptics_rmi4_hover_finger_en_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
if (!prox)
return -ENODEV;
return snprintf(buf, PAGE_SIZE, "%u\n",
prox->hover_finger_en);
}
static ssize_t synaptics_rmi4_hover_finger_en_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
unsigned int input;
struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
if (!prox)
return -ENODEV;
if (kstrtouint(buf, 16, &input) != 1)
return -EINVAL;
if (input == 1)
prox->hover_finger_en = true;
else if (input == 0)
prox->hover_finger_en = false;
else
return -EINVAL;
retval = prox_set_hover_finger_en();
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to change hovering finger enable setting\n",
__func__);
return retval;
}
return count;
}
int synaptics_rmi4_prox_hover_finger_en(bool enable)
{
int retval;
if (!prox)
return -ENODEV;
prox->hover_finger_en = enable;
retval = prox_set_hover_finger_en();
if (retval < 0)
return retval;
return 0;
}
EXPORT_SYMBOL(synaptics_rmi4_prox_hover_finger_en);
static void synaptics_rmi4_prox_attn(struct synaptics_rmi4_data *rmi4_data,
unsigned char intr_mask)
{
if (!prox)
return;
if (prox->intr_mask & intr_mask)
prox_hover_finger_report();
return;
}
static int synaptics_rmi4_prox_init(struct synaptics_rmi4_data *rmi4_data)
{
int retval;
unsigned char attr_count;
if (prox) {
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Handle already exists\n",
__func__);
return 0;
}
prox = kzalloc(sizeof(*prox), GFP_KERNEL);
if (!prox) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for prox\n",
__func__);
retval = -ENOMEM;
goto exit;
}
prox->finger_data = kzalloc(sizeof(*(prox->finger_data)), GFP_KERNEL);
if (!prox->finger_data) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for finger_data\n",
__func__);
retval = -ENOMEM;
goto exit_free_prox;
}
prox->rmi4_data = rmi4_data;
retval = prox_scan_pdt();
if (retval < 0)
goto exit_free_finger_data;
prox->hover_finger_en = true;
retval = prox_set_hover_finger_en();
if (retval < 0)
return retval;
prox->prox_dev = input_allocate_device();
if (prox->prox_dev == NULL) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to allocate proximity device\n",
__func__);
retval = -ENOMEM;
goto exit_free_finger_data;
}
prox->prox_dev->name = PROXIMITY_DRIVER_NAME;
prox->prox_dev->phys = PROX_PHYS_NAME;
prox->prox_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT;
prox->prox_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION;
prox->prox_dev->dev.parent = rmi4_data->pdev->dev.parent;
input_set_drvdata(prox->prox_dev, rmi4_data);
set_bit(EV_KEY, prox->prox_dev->evbit);
set_bit(EV_ABS, prox->prox_dev->evbit);
set_bit(BTN_TOUCH, prox->prox_dev->keybit);
set_bit(BTN_TOOL_FINGER, prox->prox_dev->keybit);
#ifdef INPUT_PROP_DIRECT
set_bit(INPUT_PROP_DIRECT, prox->prox_dev->propbit);
#endif
prox_set_params();
retval = input_register_device(prox->prox_dev);
if (retval) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to register proximity device\n",
__func__);
goto exit_free_input_device;
}
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj,
&attrs[attr_count].attr);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to create sysfs attributes\n",
__func__);
goto exit_free_sysfs;
}
}
return 0;
exit_free_sysfs:
for (attr_count--; attr_count >= 0; attr_count--) {
sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
&attrs[attr_count].attr);
}
input_unregister_device(prox->prox_dev);
prox->prox_dev = NULL;
exit_free_input_device:
if (prox->prox_dev)
input_free_device(prox->prox_dev);
exit_free_finger_data:
kfree(prox->finger_data);
exit_free_prox:
kfree(prox);
prox = NULL;
exit:
return retval;
}
static void synaptics_rmi4_prox_remove(struct synaptics_rmi4_data *rmi4_data)
{
unsigned char attr_count;
if (!prox)
goto exit;
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
&attrs[attr_count].attr);
}
input_unregister_device(prox->prox_dev);
kfree(prox->finger_data);
kfree(prox);
prox = NULL;
exit:
complete(&prox_remove_complete);
return;
}
static void synaptics_rmi4_prox_reset(struct synaptics_rmi4_data *rmi4_data)
{
if (!prox) {
synaptics_rmi4_prox_init(rmi4_data);
return;
}
prox_hover_finger_lift();
prox_scan_pdt();
prox_set_hover_finger_en();
return;
}
static void synaptics_rmi4_prox_reinit(struct synaptics_rmi4_data *rmi4_data)
{
if (!prox)
return;
prox_hover_finger_lift();
prox_set_hover_finger_en();
return;
}
static void synaptics_rmi4_prox_e_suspend(struct synaptics_rmi4_data *rmi4_data)
{
if (!prox)
return;
prox_hover_finger_lift();
return;
}
static void synaptics_rmi4_prox_suspend(struct synaptics_rmi4_data *rmi4_data)
{
if (!prox)
return;
prox_hover_finger_lift();
return;
}
static struct synaptics_rmi4_exp_fn proximity_module = {
.fn_type = RMI_PROXIMITY,
.init = synaptics_rmi4_prox_init,
.remove = synaptics_rmi4_prox_remove,
.reset = synaptics_rmi4_prox_reset,
.reinit = synaptics_rmi4_prox_reinit,
.early_suspend = synaptics_rmi4_prox_e_suspend,
.suspend = synaptics_rmi4_prox_suspend,
.resume = NULL,
.late_resume = NULL,
.attn = synaptics_rmi4_prox_attn,
};
static int __init rmi4_proximity_module_init(void)
{
synaptics_rmi4_new_function(&proximity_module, true);
return 0;
}
static void __exit rmi4_proximity_module_exit(void)
{
synaptics_rmi4_new_function(&proximity_module, false);
wait_for_completion(&prox_remove_complete);
return;
}
module_init(rmi4_proximity_module_init);
module_exit(rmi4_proximity_module_exit);
MODULE_AUTHOR("Synaptics, Inc.");
MODULE_DESCRIPTION("Synaptics DSX Proximity Module");
MODULE_LICENSE("GPL v2");

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,713 @@
/*
* Synaptics DSX touchscreen driver
*
* Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved.
*
* Copyright (c) 2018 The Linux Foundation. All rights reserved.
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
*
* 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.
*
* INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
* EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
* AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
* IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
* WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
* AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
* NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
* TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
* DOLLARS.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/types.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/input/synaptics_dsx.h>
#include "synaptics_dsx_core.h"
#define SPI_READ 0x80
#define SPI_WRITE 0x00
static unsigned char *buf;
static struct spi_transfer *xfer;
#ifdef CONFIG_OF
static int parse_dt(struct device *dev, struct synaptics_dsx_board_data *bdata)
{
int retval;
u32 value;
const char *name;
struct property *prop;
struct device_node *np = dev->of_node;
bdata->irq_gpio = of_get_named_gpio_flags(np,
"synaptics,irq-gpio", 0,
(enum of_gpio_flags *)&bdata->irq_flags);
retval = of_property_read_u32(np, "synaptics,irq-on-state",
&value);
if (retval < 0)
bdata->irq_on_state = 0;
else
bdata->irq_on_state = value;
retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name);
if (retval < 0)
bdata->pwr_reg_name = NULL;
else
bdata->pwr_reg_name = name;
retval = of_property_read_string(np, "synaptics,bus-reg-name", &name);
if (retval < 0)
bdata->bus_reg_name = NULL;
else
bdata->bus_reg_name = name;
prop = of_find_property(np, "synaptics,power-gpio", NULL);
if (prop && prop->length) {
bdata->power_gpio = of_get_named_gpio_flags(np,
"synaptics,power-gpio", 0, NULL);
retval = of_property_read_u32(np, "synaptics,power-on-state",
&value);
if (retval < 0) {
dev_err(dev, "%s: Unable to read synaptics,power-on-state property\n",
__func__);
return retval;
} else {
bdata->power_on_state = value;
}
} else {
bdata->power_gpio = -1;
}
prop = of_find_property(np, "synaptics,power-delay-ms", NULL);
if (prop && prop->length) {
retval = of_property_read_u32(np, "synaptics,power-delay-ms",
&value);
if (retval < 0) {
dev_err(dev, "%s: Unable to read synaptics,power-delay-ms property\n",
__func__);
return retval;
} else {
bdata->power_delay_ms = value;
}
} else {
bdata->power_delay_ms = 0;
}
prop = of_find_property(np, "synaptics,reset-gpio", NULL);
if (prop && prop->length) {
bdata->reset_gpio = of_get_named_gpio_flags(np,
"synaptics,reset-gpio", 0, NULL);
retval = of_property_read_u32(np, "synaptics,reset-on-state",
&value);
if (retval < 0) {
dev_err(dev, "%s: Unable to read synaptics,reset-on-state property\n",
__func__);
return retval;
} else {
bdata->reset_on_state = value;
}
retval = of_property_read_u32(np, "synaptics,reset-active-ms",
&value);
if (retval < 0) {
dev_err(dev, "%s: Unable to read synaptics,reset-active-ms property\n",
__func__);
return retval;
} else {
bdata->reset_active_ms = value;
}
} else {
bdata->reset_gpio = -1;
}
prop = of_find_property(np, "synaptics,reset-delay-ms", NULL);
if (prop && prop->length) {
retval = of_property_read_u32(np, "synaptics,reset-delay-ms",
&value);
if (retval < 0) {
dev_err(dev, "%s: Unable to read synaptics,reset-delay-ms property\n",
__func__);
return retval;
} else {
bdata->reset_delay_ms = value;
}
} else {
bdata->reset_delay_ms = 0;
}
prop = of_find_property(np, "synaptics,byte-delay-us", NULL);
if (prop && prop->length) {
retval = of_property_read_u32(np, "synaptics,byte-delay-us",
&value);
if (retval < 0) {
dev_err(dev, "%s: Unable to read synaptics,byte-delay-us property\n",
__func__);
return retval;
} else {
bdata->byte_delay_us = value;
}
} else {
bdata->byte_delay_us = 0;
}
prop = of_find_property(np, "synaptics,block-delay-us", NULL);
if (prop && prop->length) {
retval = of_property_read_u32(np, "synaptics,block-delay-us",
&value);
if (retval < 0) {
dev_err(dev, "%s: Unable to read synaptics,block-delay-us property\n",
__func__);
return retval;
} else {
bdata->block_delay_us = value;
}
} else {
bdata->block_delay_us = 0;
}
prop = of_find_property(np, "synaptics,address-delay-us", NULL);
if (prop && prop->length) {
retval = of_property_read_u32(np, "synaptics,address-delay-us",
&value);
if (retval < 0) {
dev_err(dev, "%s: Unable to read synaptics,address-delay-us property\n",
__func__);
return retval;
} else {
bdata->addr_delay_us = value;
}
} else {
bdata->addr_delay_us = 0;
}
prop = of_find_property(np, "synaptics,max-y-for-2d", NULL);
if (prop && prop->length) {
retval = of_property_read_u32(np, "synaptics,max-y-for-2d",
&value);
if (retval < 0) {
dev_err(dev, "%s: Unable to read synaptics,max-y-for-2d property\n",
__func__);
return retval;
} else {
bdata->max_y_for_2d = value;
}
} else {
bdata->max_y_for_2d = -1;
}
prop = of_find_property(np, "synaptics,swap-axes", NULL);
bdata->swap_axes = prop > 0 ? true : false;
prop = of_find_property(np, "synaptics,x-flip", NULL);
bdata->x_flip = prop > 0 ? true : false;
prop = of_find_property(np, "synaptics,y-flip", NULL);
bdata->y_flip = prop > 0 ? true : false;
prop = of_find_property(np, "synaptics,ub-i2c-addr", NULL);
if (prop && prop->length) {
retval = of_property_read_u32(np, "synaptics,ub-i2c-addr",
&value);
if (retval < 0) {
dev_err(dev, "%s: Unable to read synaptics,ub-i2c-addr property\n",
__func__);
return retval;
} else {
bdata->ub_i2c_addr = (unsigned short)value;
}
} else {
bdata->ub_i2c_addr = -1;
}
prop = of_find_property(np, "synaptics,cap-button-codes", NULL);
if (prop && prop->length) {
bdata->cap_button_map->map = devm_kzalloc(dev,
prop->length,
GFP_KERNEL);
if (!bdata->cap_button_map->map)
return -ENOMEM;
bdata->cap_button_map->nbuttons = prop->length / sizeof(u32);
retval = of_property_read_u32_array(np,
"synaptics,cap-button-codes",
bdata->cap_button_map->map,
bdata->cap_button_map->nbuttons);
if (retval < 0) {
bdata->cap_button_map->nbuttons = 0;
bdata->cap_button_map->map = NULL;
}
} else {
bdata->cap_button_map->nbuttons = 0;
bdata->cap_button_map->map = NULL;
}
prop = of_find_property(np, "synaptics,vir-button-codes", NULL);
if (prop && prop->length) {
bdata->vir_button_map->map = devm_kzalloc(dev,
prop->length,
GFP_KERNEL);
if (!bdata->vir_button_map->map)
return -ENOMEM;
bdata->vir_button_map->nbuttons = prop->length / sizeof(u32);
bdata->vir_button_map->nbuttons /= 5;
retval = of_property_read_u32_array(np,
"synaptics,vir-button-codes",
bdata->vir_button_map->map,
bdata->vir_button_map->nbuttons * 5);
if (retval < 0) {
bdata->vir_button_map->nbuttons = 0;
bdata->vir_button_map->map = NULL;
}
} else {
bdata->vir_button_map->nbuttons = 0;
bdata->vir_button_map->map = NULL;
}
return 0;
}
#endif
static int synaptics_rmi4_spi_alloc_buf(struct synaptics_rmi4_data *rmi4_data,
unsigned int size, unsigned int count)
{
static unsigned int buf_size;
static unsigned int xfer_count;
if (size > buf_size) {
if (buf_size)
kfree(buf);
buf = kmalloc(size, GFP_KERNEL);
if (!buf) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for buf\n",
__func__);
buf_size = 0;
return -ENOMEM;
}
buf_size = size;
}
if (count > xfer_count) {
if (xfer_count)
kfree(xfer);
xfer = kcalloc(count, sizeof(struct spi_transfer), GFP_KERNEL);
if (!xfer) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for xfer\n",
__func__);
xfer_count = 0;
return -ENOMEM;
}
xfer_count = count;
} else {
memset(xfer, 0, count * sizeof(struct spi_transfer));
}
return 0;
}
static int synaptics_rmi4_spi_set_page(struct synaptics_rmi4_data *rmi4_data,
unsigned short addr)
{
int retval;
unsigned int index;
unsigned int byte_count = PAGE_SELECT_LEN + 1;
unsigned char page;
struct spi_message msg;
struct spi_device *spi = to_spi_device(rmi4_data->pdev->dev.parent);
const struct synaptics_dsx_board_data *bdata =
rmi4_data->hw_if->board_data;
page = ((addr >> 8) & MASK_8BIT);
if ((page >> 7) == (rmi4_data->current_page >> 7))
return PAGE_SELECT_LEN;
spi_message_init(&msg);
retval = synaptics_rmi4_spi_alloc_buf(rmi4_data, byte_count,
byte_count);
if (retval < 0)
return retval;
buf[0] = SPI_WRITE;
buf[1] = MASK_8BIT;
buf[2] = page;
if (bdata->byte_delay_us == 0) {
xfer[0].len = byte_count;
xfer[0].tx_buf = &buf[0];
if (bdata->block_delay_us)
xfer[0].delay_usecs = bdata->block_delay_us;
spi_message_add_tail(&xfer[0], &msg);
} else {
for (index = 0; index < byte_count; index++) {
xfer[index].len = 1;
xfer[index].tx_buf = &buf[index];
if (index == 1)
xfer[index].delay_usecs = bdata->addr_delay_us;
else
xfer[index].delay_usecs = bdata->byte_delay_us;
spi_message_add_tail(&xfer[index], &msg);
}
if (bdata->block_delay_us)
xfer[index - 1].delay_usecs = bdata->block_delay_us;
}
retval = spi_sync(spi, &msg);
if (retval == 0) {
rmi4_data->current_page = page;
retval = PAGE_SELECT_LEN;
} else {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to complete SPI transfer, error = %d\n",
__func__, retval);
}
return retval;
}
static int synaptics_rmi4_spi_read(struct synaptics_rmi4_data *rmi4_data,
unsigned short addr, unsigned char *data, unsigned int length)
{
int retval;
unsigned int index;
unsigned int byte_count = length + ADDRESS_LEN;
unsigned char txbuf[ADDRESS_LEN];
struct spi_message msg;
struct spi_device *spi = to_spi_device(rmi4_data->pdev->dev.parent);
const struct synaptics_dsx_board_data *bdata =
rmi4_data->hw_if->board_data;
spi_message_init(&msg);
txbuf[0] = (addr >> 8) | SPI_READ;
txbuf[1] = addr & MASK_8BIT;
mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
retval = synaptics_rmi4_spi_set_page(rmi4_data, addr);
if (retval != PAGE_SELECT_LEN) {
mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
return -EIO;
}
if (bdata->byte_delay_us == 0) {
retval = synaptics_rmi4_spi_alloc_buf(rmi4_data, length,
2);
} else {
retval = synaptics_rmi4_spi_alloc_buf(rmi4_data, length,
byte_count);
}
if (retval < 0) {
mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
return retval;
}
if (bdata->byte_delay_us == 0) {
xfer[0].len = ADDRESS_LEN;
xfer[0].tx_buf = &txbuf[0];
spi_message_add_tail(&xfer[0], &msg);
xfer[1].len = length;
xfer[1].rx_buf = &buf[0];
if (bdata->block_delay_us)
xfer[1].delay_usecs = bdata->block_delay_us;
spi_message_add_tail(&xfer[1], &msg);
} else {
for (index = 0; index < byte_count; index++) {
xfer[index].len = 1;
if (index < ADDRESS_LEN)
xfer[index].tx_buf = &txbuf[index];
else
xfer[index].rx_buf = &buf[index - ADDRESS_LEN];
if (index == 1)
xfer[index].delay_usecs = bdata->addr_delay_us;
else
xfer[index].delay_usecs = bdata->byte_delay_us;
spi_message_add_tail(&xfer[index], &msg);
}
if (bdata->block_delay_us)
xfer[index - 1].delay_usecs = bdata->block_delay_us;
}
retval = spi_sync(spi, &msg);
if (retval == 0) {
retval = secure_memcpy(data, length, buf, length, length);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to copy data\n",
__func__);
} else {
retval = length;
}
} else {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to complete SPI transfer, error = %d\n",
__func__, retval);
}
mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
return retval;
}
static int synaptics_rmi4_spi_write(struct synaptics_rmi4_data *rmi4_data,
unsigned short addr, unsigned char *data, unsigned int length)
{
int retval;
unsigned int index;
unsigned int byte_count = length + ADDRESS_LEN;
struct spi_message msg;
struct spi_device *spi = to_spi_device(rmi4_data->pdev->dev.parent);
const struct synaptics_dsx_board_data *bdata =
rmi4_data->hw_if->board_data;
spi_message_init(&msg);
mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
retval = synaptics_rmi4_spi_set_page(rmi4_data, addr);
if (retval != PAGE_SELECT_LEN) {
mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
return -EIO;
}
if (bdata->byte_delay_us == 0) {
retval = synaptics_rmi4_spi_alloc_buf(rmi4_data, byte_count,
1);
} else {
retval = synaptics_rmi4_spi_alloc_buf(rmi4_data, byte_count,
byte_count);
}
if (retval < 0) {
mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
return retval;
}
buf[0] = (addr >> 8) & ~SPI_READ;
buf[1] = addr & MASK_8BIT;
retval = secure_memcpy(&buf[ADDRESS_LEN],
byte_count - ADDRESS_LEN, data, length, length);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to copy data\n",
__func__);
mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
return retval;
}
if (bdata->byte_delay_us == 0) {
xfer[0].len = byte_count;
xfer[0].tx_buf = &buf[0];
if (bdata->block_delay_us)
xfer[0].delay_usecs = bdata->block_delay_us;
spi_message_add_tail(xfer, &msg);
} else {
for (index = 0; index < byte_count; index++) {
xfer[index].len = 1;
xfer[index].tx_buf = &buf[index];
if (index == 1)
xfer[index].delay_usecs = bdata->addr_delay_us;
else
xfer[index].delay_usecs = bdata->byte_delay_us;
spi_message_add_tail(&xfer[index], &msg);
}
if (bdata->block_delay_us)
xfer[index - 1].delay_usecs = bdata->block_delay_us;
}
retval = spi_sync(spi, &msg);
if (retval == 0) {
retval = length;
} else {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to complete SPI transfer, error = %d\n",
__func__, retval);
}
mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
return retval;
}
static struct synaptics_dsx_bus_access bus_access = {
.type = BUS_SPI,
.read = synaptics_rmi4_spi_read,
.write = synaptics_rmi4_spi_write,
};
static struct synaptics_dsx_hw_interface hw_if;
static struct platform_device *synaptics_dsx_spi_device;
static void synaptics_rmi4_spi_dev_release(struct device *dev)
{
kfree(synaptics_dsx_spi_device);
return;
}
static int synaptics_rmi4_spi_probe(struct spi_device *spi)
{
int retval;
if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) {
dev_err(&spi->dev,
"%s: Full duplex not supported by host\n",
__func__);
return -EIO;
}
synaptics_dsx_spi_device = kzalloc(
sizeof(struct platform_device),
GFP_KERNEL);
if (!synaptics_dsx_spi_device) {
dev_err(&spi->dev,
"%s: Failed to allocate memory for synaptics_dsx_spi_device\n",
__func__);
return -ENOMEM;
}
#ifdef CONFIG_OF
if (spi->dev.of_node) {
hw_if.board_data = devm_kzalloc(&spi->dev,
sizeof(struct synaptics_dsx_board_data),
GFP_KERNEL);
if (!hw_if.board_data) {
dev_err(&spi->dev,
"%s: Failed to allocate memory for board data\n",
__func__);
return -ENOMEM;
}
hw_if.board_data->cap_button_map = devm_kzalloc(&spi->dev,
sizeof(struct synaptics_dsx_button_map),
GFP_KERNEL);
if (!hw_if.board_data->cap_button_map) {
dev_err(&spi->dev,
"%s: Failed to allocate memory for 0D button map\n",
__func__);
return -ENOMEM;
}
hw_if.board_data->vir_button_map = devm_kzalloc(&spi->dev,
sizeof(struct synaptics_dsx_button_map),
GFP_KERNEL);
if (!hw_if.board_data->vir_button_map) {
dev_err(&spi->dev,
"%s: Failed to allocate memory for virtual button map\n",
__func__);
return -ENOMEM;
}
parse_dt(&spi->dev, hw_if.board_data);
}
#else
hw_if.board_data = spi->dev.platform_data;
#endif
hw_if.bus_access = &bus_access;
spi->bits_per_word = 8;
spi->mode = SPI_MODE_3;
retval = spi_setup(spi);
if (retval < 0) {
dev_err(&spi->dev,
"%s: Failed to perform SPI setup\n",
__func__);
return retval;
}
synaptics_dsx_spi_device->name = PLATFORM_DRIVER_NAME;
synaptics_dsx_spi_device->id = 0;
synaptics_dsx_spi_device->num_resources = 0;
synaptics_dsx_spi_device->dev.parent = &spi->dev;
synaptics_dsx_spi_device->dev.platform_data = &hw_if;
synaptics_dsx_spi_device->dev.release = synaptics_rmi4_spi_dev_release;
retval = platform_device_register(synaptics_dsx_spi_device);
if (retval) {
dev_err(&spi->dev,
"%s: Failed to register platform device\n",
__func__);
return -ENODEV;
}
return 0;
}
static int synaptics_rmi4_spi_remove(struct spi_device *spi)
{
platform_device_unregister(synaptics_dsx_spi_device);
return 0;
}
static const struct spi_device_id synaptics_rmi4_id_table[] = {
{SPI_DRIVER_NAME, 0},
{},
};
MODULE_DEVICE_TABLE(spi, synaptics_rmi4_id_table);
#ifdef CONFIG_OF
static struct of_device_id synaptics_rmi4_of_match_table[] = {
{
.compatible = "synaptics,dsx-spi",
},
{},
};
MODULE_DEVICE_TABLE(of, synaptics_rmi4_of_match_table);
#else
#define synaptics_rmi4_of_match_table NULL
#endif
static struct spi_driver synaptics_rmi4_spi_driver = {
.driver = {
.name = SPI_DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = synaptics_rmi4_of_match_table,
},
.probe = synaptics_rmi4_spi_probe,
.remove = synaptics_rmi4_spi_remove,
.id_table = synaptics_rmi4_id_table,
};
int synaptics_rmi4_bus_init(void)
{
return spi_register_driver(&synaptics_rmi4_spi_driver);
}
EXPORT_SYMBOL(synaptics_rmi4_bus_init);
void synaptics_rmi4_bus_exit(void)
{
kfree(buf);
kfree(xfer);
spi_unregister_driver(&synaptics_rmi4_spi_driver);
return;
}
EXPORT_SYMBOL(synaptics_rmi4_bus_exit);
MODULE_AUTHOR("Synaptics, Inc.");
MODULE_DESCRIPTION("Synaptics DSX SPI Bus Support Module");
MODULE_LICENSE("GPL v2");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,417 @@
/*
* Synaptics DSX touchscreen driver
*
* Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved.
*
* Copyright (c) 2018 The Linux Foundation. All rights reserved.
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
*
* 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.
*
* INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
* EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
* AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
* IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
* WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
* AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
* NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
* TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
* DOLLARS.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/input/synaptics_dsx.h>
#include "synaptics_dsx_core.h"
#define SYSFS_FOLDER_NAME "video"
/*
*#define RMI_DCS_SUSPEND_RESUME
*/
static ssize_t video_sysfs_dcs_write_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t video_sysfs_param_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static int video_send_dcs_command(unsigned char command_opcode);
struct f38_command {
union {
struct {
unsigned char command_opcode;
unsigned char register_access:1;
unsigned char gamma_page:1;
unsigned char f38_control1_b2__7:6;
unsigned char parameter_field_1;
unsigned char parameter_field_2;
unsigned char parameter_field_3;
unsigned char parameter_field_4;
unsigned char send_to_dcs:1;
unsigned char f38_command6_b1__7:7;
} __packed;
unsigned char data[7];
};
};
struct synaptics_rmi4_video_handle {
unsigned char param;
unsigned short query_base_addr;
unsigned short control_base_addr;
unsigned short data_base_addr;
unsigned short command_base_addr;
struct synaptics_rmi4_data *rmi4_data;
struct kobject *sysfs_dir;
};
#ifdef RMI_DCS_SUSPEND_RESUME
struct dcs_command {
unsigned char command;
unsigned int wait_time;
};
static struct dcs_command suspend_sequence[] = {
{
.command = 0x28,
.wait_time = 200,
},
{
.command = 0x10,
.wait_time = 200,
},
};
static struct dcs_command resume_sequence[] = {
{
.command = 0x11,
.wait_time = 200,
},
{
.command = 0x29,
.wait_time = 200,
},
};
#endif
static struct device_attribute attrs[] = {
__ATTR(dcs_write, 0220,
synaptics_rmi4_show_error,
video_sysfs_dcs_write_store),
__ATTR(param, 0220,
synaptics_rmi4_show_error,
video_sysfs_param_store),
};
static struct synaptics_rmi4_video_handle *video;
DECLARE_COMPLETION(video_remove_complete);
static ssize_t video_sysfs_dcs_write_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
unsigned int input;
if (kstrtouint(buf, 16, &input) != 1)
return -EINVAL;
retval = video_send_dcs_command((unsigned char)input);
if (retval < 0)
return retval;
return count;
}
static ssize_t video_sysfs_param_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned int input;
if (kstrtouint(buf, 16, &input) != 1)
return -EINVAL;
video->param = (unsigned char)input;
return count;
}
static int video_send_dcs_command(unsigned char command_opcode)
{
int retval;
struct f38_command command;
struct synaptics_rmi4_data *rmi4_data = video->rmi4_data;
memset(&command, 0x00, sizeof(command));
command.command_opcode = command_opcode;
command.parameter_field_1 = video->param;
command.send_to_dcs = 1;
video->param = 0;
retval = synaptics_rmi4_reg_write(rmi4_data,
video->command_base_addr,
command.data,
sizeof(command.data));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to send DCS command\n",
__func__);
return retval;
}
return 0;
}
static int video_scan_pdt(void)
{
int retval;
unsigned char page;
unsigned short addr;
bool f38_found = false;
struct synaptics_rmi4_fn_desc rmi_fd;
struct synaptics_rmi4_data *rmi4_data = video->rmi4_data;
for (page = 0; page < PAGES_TO_SERVICE; page++) {
for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) {
addr |= (page << 8);
retval = synaptics_rmi4_reg_read(rmi4_data,
addr,
(unsigned char *)&rmi_fd,
sizeof(rmi_fd));
if (retval < 0)
return retval;
addr &= ~(MASK_8BIT << 8);
if (!rmi_fd.fn_number)
break;
if (rmi_fd.fn_number == SYNAPTICS_RMI4_F38) {
f38_found = true;
goto f38_found;
}
}
}
if (!f38_found) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to find F38\n",
__func__);
return -EINVAL;
}
f38_found:
video->query_base_addr = rmi_fd.query_base_addr | (page << 8);
video->control_base_addr = rmi_fd.ctrl_base_addr | (page << 8);
video->data_base_addr = rmi_fd.data_base_addr | (page << 8);
video->command_base_addr = rmi_fd.cmd_base_addr | (page << 8);
return 0;
}
static int synaptics_rmi4_video_init(struct synaptics_rmi4_data *rmi4_data)
{
int retval;
unsigned char attr_count;
if (video) {
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Handle already exists\n",
__func__);
return 0;
}
video = kzalloc(sizeof(*video), GFP_KERNEL);
if (!video) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for video\n",
__func__);
retval = -ENOMEM;
goto exit;
}
video->rmi4_data = rmi4_data;
retval = video_scan_pdt();
if (retval < 0) {
retval = 0;
goto exit_scan_pdt;
}
video->sysfs_dir = kobject_create_and_add(SYSFS_FOLDER_NAME,
&rmi4_data->input_dev->dev.kobj);
if (!video->sysfs_dir) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to create sysfs directory\n",
__func__);
retval = -ENODEV;
goto exit_sysfs_dir;
}
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
retval = sysfs_create_file(video->sysfs_dir,
&attrs[attr_count].attr);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to create sysfs attributes\n",
__func__);
retval = -ENODEV;
goto exit_sysfs_attrs;
}
}
return 0;
exit_sysfs_attrs:
for (attr_count--; attr_count >= 0; attr_count--)
sysfs_remove_file(video->sysfs_dir, &attrs[attr_count].attr);
kobject_put(video->sysfs_dir);
exit_sysfs_dir:
exit_scan_pdt:
kfree(video);
video = NULL;
exit:
return retval;
}
static void synaptics_rmi4_video_remove(struct synaptics_rmi4_data *rmi4_data)
{
unsigned char attr_count;
if (!video)
goto exit;
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
sysfs_remove_file(video->sysfs_dir, &attrs[attr_count].attr);
kobject_put(video->sysfs_dir);
kfree(video);
video = NULL;
exit:
complete(&video_remove_complete);
return;
}
static void synaptics_rmi4_video_reset(struct synaptics_rmi4_data *rmi4_data)
{
if (!video)
synaptics_rmi4_video_init(rmi4_data);
return;
}
#ifdef RMI_DCS_SUSPEND_RESUME
static void synaptics_rmi4_video_suspend(struct synaptics_rmi4_data *rmi4_data)
{
int retval;
unsigned char ii;
unsigned char command;
unsigned char num_of_cmds;
if (!video)
return;
num_of_cmds = ARRAY_SIZE(suspend_sequence);
for (ii = 0; ii < num_of_cmds; ii++) {
command = suspend_sequence[ii].command;
retval = video_send_dcs_command(command);
if (retval < 0)
return;
msleep(suspend_sequence[ii].wait_time);
}
return;
}
static void synaptics_rmi4_video_resume(struct synaptics_rmi4_data *rmi4_data)
{
int retval;
unsigned char ii;
unsigned char command;
unsigned char num_of_cmds;
if (!video)
return;
num_of_cmds = ARRAY_SIZE(resume_sequence);
for (ii = 0; ii < num_of_cmds; ii++) {
command = resume_sequence[ii].command;
retval = video_send_dcs_command(command);
if (retval < 0)
return;
msleep(resume_sequence[ii].wait_time);
}
return;
}
#endif
static struct synaptics_rmi4_exp_fn video_module = {
.fn_type = RMI_VIDEO,
.init = synaptics_rmi4_video_init,
.remove = synaptics_rmi4_video_remove,
.reset = synaptics_rmi4_video_reset,
.reinit = NULL,
.early_suspend = NULL,
#ifdef RMI_DCS_SUSPEND_RESUME
.suspend = synaptics_rmi4_video_suspend,
.resume = synaptics_rmi4_video_resume,
#else
.suspend = NULL,
.resume = NULL,
#endif
.late_resume = NULL,
.attn = NULL,
};
static int __init rmi4_video_module_init(void)
{
synaptics_rmi4_new_function(&video_module, true);
return 0;
}
static void __exit rmi4_video_module_exit(void)
{
synaptics_rmi4_new_function(&video_module, false);
wait_for_completion(&video_remove_complete);
return;
}
module_init(rmi4_video_module_init);
module_exit(rmi4_video_module_exit);
MODULE_AUTHOR("Synaptics, Inc.");
MODULE_DESCRIPTION("Synaptics DSX Video Module");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,114 @@
/*
* Synaptics DSX touchscreen driver
*
* Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved.
*
* Copyright (c) 2018 The Linux Foundation. All rights reserved.
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
*
* 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.
*
* INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
* EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
* AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
* IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
* WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
* AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
* NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
* TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
* DOLLARS.
*/
#ifndef _SYNAPTICS_DSX_H_
#define _SYNAPTICS_DSX_H_
#define PLATFORM_DRIVER_NAME "synaptics_dsx"
#define STYLUS_DRIVER_NAME "synaptics_dsx_stylus"
#define ACTIVE_PEN_DRIVER_NAME "synaptics_dsx_active_pen"
#define PROXIMITY_DRIVER_NAME "synaptics_dsx_proximity"
#define GESTURE_DRIVER_NAME "synaptics_dsx_gesture"
#define I2C_DRIVER_NAME "synaptics_dsx_i2c"
#define SPI_DRIVER_NAME "synaptics_dsx_spi"
/*
* struct synaptics_dsx_button_map - button map
* @nbuttons: number of buttons
* @map: pointer to array of button codes
*/
struct synaptics_dsx_button_map {
unsigned char nbuttons;
unsigned int *map;
};
/*
* struct synaptics_dsx_board_data - DSX board data
* @x_flip: x flip flag
* @y_flip: y flip flag
* @swap_axes: swap axes flag
* @irq_gpio: attention interrupt GPIO
* @irq_on_state: attention interrupt active state
* @power_gpio: power switch GPIO
* @power_on_state: power switch active state
* @reset_gpio: reset GPIO
* @reset_on_state: reset active state
* @max_y_for_2d: maximum y value for 2D area when virtual buttons are present
* @irq_flags: IRQ flags
* @i2c_addr: I2C slave address
* @ub_i2c_addr: microbootloader mode I2C slave address
* @device_descriptor_addr: HID device descriptor address
* @panel_x: x-axis resolution of display panel
* @panel_y: y-axis resolution of display panel
* @power_delay_ms: delay time to wait after powering up device
* @reset_delay_ms: delay time to wait after resetting device
* @reset_active_ms: reset active time
* @byte_delay_us: delay time between two bytes of SPI data
* @block_delay_us: delay time between two SPI transfers
* @addr_delay_us: delay time after sending address word
* @pwr_reg_name: pointer to name of regulator for power control
* @bus_reg_name: pointer to name of regulator for bus pullup control
* @cap_button_map: pointer to 0D button map
* @vir_button_map: pointer to virtual button map
*/
struct synaptics_dsx_board_data {
bool x_flip;
bool y_flip;
bool swap_axes;
int irq_gpio;
int irq_on_state;
int power_gpio;
int power_on_state;
int reset_gpio;
int reset_on_state;
int max_y_for_2d;
unsigned long irq_flags;
unsigned short i2c_addr;
unsigned short ub_i2c_addr;
unsigned short device_descriptor_addr;
unsigned int panel_x;
unsigned int panel_y;
unsigned int power_delay_ms;
unsigned int reset_delay_ms;
unsigned int reset_active_ms;
unsigned int byte_delay_us;
unsigned int block_delay_us;
unsigned int addr_delay_us;
const char *pwr_reg_name;
const char *bus_reg_name;
struct synaptics_dsx_button_map *cap_button_map;
struct synaptics_dsx_button_map *vir_button_map;
};
#endif