input: synaptics_dsx: Add support for synaptics_dsx touch
Change-Id: I1df8a7465fdb5c18cf69e0908347fc78ce7dd07c Git-commit: 67b7a9d78725d5de2a1899095eb0026e15343a62 Git-repo: git://github.com/synaptics-touch/synaptics-dsx-v2.1 Signed-off-by: Alexandra Chin <alexandra.chin@tw.synaptics.com> Signed-off-by: Amy Maloche <amaloche@codeaurora.org>
This commit is contained in:
parent
12cf0ae819
commit
7ef5f9cf4e
10 changed files with 7469 additions and 0 deletions
70
drivers/input/touchscreen/synaptics_dsx/Kconfig
Normal file
70
drivers/input/touchscreen/synaptics_dsx/Kconfig
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
#
|
||||||
|
# Synaptics DSX touchscreen driver configuration
|
||||||
|
#
|
||||||
|
menuconfig TOUCHSCREEN_SYNAPTICS_DSX
|
||||||
|
bool "Synaptics DSX touchscreen"
|
||||||
|
default y
|
||||||
|
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 touchscreen bus interface"
|
||||||
|
config TOUCHSCREEN_SYNAPTICS_DSX_I2C
|
||||||
|
bool "I2C"
|
||||||
|
depends on I2C
|
||||||
|
config TOUCHSCREEN_SYNAPTICS_DSX_SPI
|
||||||
|
bool "SPI"
|
||||||
|
depends on SPI_MASTER
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config TOUCHSCREEN_SYNAPTICS_DSX_CORE
|
||||||
|
tristate "Synaptics DSX core driver module"
|
||||||
|
depends on TOUCHSCREEN_SYNAPTICS_DSX_I2C || TOUCHSCREEN_SYNAPTICS_DSX_SPI
|
||||||
|
help
|
||||||
|
Say Y here to enable basic touch reporting functionalities.
|
||||||
|
|
||||||
|
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 touchscreen RMI device module"
|
||||||
|
depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
|
||||||
|
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 touchscreen firmware update module"
|
||||||
|
depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
|
||||||
|
help
|
||||||
|
Say Y here to enable support for carrying out 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_PROXIMITY
|
||||||
|
tristate "Synaptics DSX touchscreen proximity module"
|
||||||
|
depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
|
||||||
|
help
|
||||||
|
Say Y here to enable support for proximity functionalities.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called synaptics_dsx_proximity.
|
||||||
|
|
||||||
|
endif
|
12
drivers/input/touchscreen/synaptics_dsx/Makefile
Normal file
12
drivers/input/touchscreen/synaptics_dsx/Makefile
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#
|
||||||
|
# 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_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_PROXIMITY) += synaptics_dsx_proximity.o
|
3063
drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c
Executable file
3063
drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c
Executable file
File diff suppressed because it is too large
Load diff
336
drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h
Executable file
336
drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h
Executable file
|
@ -0,0 +1,336 @@
|
||||||
|
/*
|
||||||
|
* Synaptics DSX touchscreen driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Synaptics Incorporated
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#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 0x2001
|
||||||
|
|
||||||
|
#include <linux/version.h>
|
||||||
|
#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
|
||||||
|
|
||||||
|
#ifdef KERNEL_ABOVE_2_6_38
|
||||||
|
#define sstrtoul(...) kstrtoul(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define sstrtoul(...) strict_strtoul(__VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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_WORD_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_F34 (0x34)
|
||||||
|
#define SYNAPTICS_RMI4_F51 (0x51)
|
||||||
|
#define SYNAPTICS_RMI4_F54 (0x54)
|
||||||
|
#define SYNAPTICS_RMI4_F55 (0x55)
|
||||||
|
|
||||||
|
#define SYNAPTICS_RMI4_PRODUCT_INFO_SIZE 2
|
||||||
|
#define SYNAPTICS_RMI4_DATE_CODE_SIZE 3
|
||||||
|
#define SYNAPTICS_RMI4_PRODUCT_ID_SIZE 10
|
||||||
|
#define SYNAPTICS_RMI4_BUILD_ID_SIZE 3
|
||||||
|
|
||||||
|
#define F12_FINGERS_TO_SUPPORT 10
|
||||||
|
#define F12_NO_OBJECT_STATUS 0x00
|
||||||
|
#define F12_FINGER_STATUS 0x01
|
||||||
|
#define F12_STYLUS_STATUS 0x02
|
||||||
|
#define F12_PALM_STATUS 0x03
|
||||||
|
#define F12_HOVERING_FINGER_STATUS 0x05
|
||||||
|
#define F12_GLOVED_FINGER_STATUS 0x06
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
enum exp_fn {
|
||||||
|
RMI_DEV = 0,
|
||||||
|
RMI_F54,
|
||||||
|
RMI_FW_UPDATER,
|
||||||
|
RMI_PROXIMITY,
|
||||||
|
RMI_ACTIVE_PEN,
|
||||||
|
RMI_LAST,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct synaptics_dsx_hw_interface {
|
||||||
|
const struct synaptics_dsx_board_data *board_data;
|
||||||
|
const struct synaptics_dsx_bus_access *bus_access;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* struct synaptics_rmi4_fn_desc - function descriptor fields in PDT
|
||||||
|
* @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_number: function number
|
||||||
|
*/
|
||||||
|
struct synaptics_rmi4_fn_desc {
|
||||||
|
unsigned char query_base_addr;
|
||||||
|
unsigned char cmd_base_addr;
|
||||||
|
unsigned char ctrl_base_addr;
|
||||||
|
unsigned char data_base_addr;
|
||||||
|
unsigned char intr_src_count;
|
||||||
|
unsigned char fn_number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 data registers
|
||||||
|
* @ctrl_base: 16-bit base address for command registers
|
||||||
|
* @data_base: 16-bit base address for control 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_f12_extra_data {
|
||||||
|
unsigned char data1_offset;
|
||||||
|
unsigned char data15_offset;
|
||||||
|
unsigned char data15_size;
|
||||||
|
unsigned char data15_data[(F12_FINGERS_TO_SUPPORT + 7) / 8];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* struct synaptics_rmi4_fn - function handler data structure
|
||||||
|
* @fn_number: function number
|
||||||
|
* @num_of_data_sources: number of data sources
|
||||||
|
* @num_of_data_points: maximum number of fingers supported
|
||||||
|
* @size_of_data_register_block: data register block size
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
struct synaptics_rmi4_fn {
|
||||||
|
unsigned char fn_number;
|
||||||
|
unsigned char num_of_data_sources;
|
||||||
|
unsigned char num_of_data_points;
|
||||||
|
unsigned char size_of_data_register_block;
|
||||||
|
unsigned char 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_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 information
|
||||||
|
* @product_info: product info array
|
||||||
|
* @date_code: device manufacture date
|
||||||
|
* @tester_id: tester id array
|
||||||
|
* @serial_number: device serial number
|
||||||
|
* @product_id_string: device product 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[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE];
|
||||||
|
unsigned char date_code[SYNAPTICS_RMI4_DATE_CODE_SIZE];
|
||||||
|
unsigned short tester_id;
|
||||||
|
unsigned short serial_number;
|
||||||
|
unsigned char product_id_string[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1];
|
||||||
|
unsigned char build_id[SYNAPTICS_RMI4_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
|
||||||
|
* @hw_if: pointer to hardware interface data
|
||||||
|
* @rmi4_mod_info: device information
|
||||||
|
* @regulator: pointer to associated regulator
|
||||||
|
* @rmi4_io_ctrl_mutex: mutex for i2c i/o control
|
||||||
|
* @early_suspend: instance to support early suspend power management
|
||||||
|
* @current_page: current page in sensor to acess
|
||||||
|
* @button_0d_enabled: flag for 0d button support
|
||||||
|
* @full_pm_cycle: flag for full power management cycle in early suspend stage
|
||||||
|
* @num_of_intr_regs: number of interrupt registers
|
||||||
|
* @f01_query_base_addr: query base address for f01
|
||||||
|
* @f01_cmd_base_addr: command base address for f01
|
||||||
|
* @f01_ctrl_base_addr: control base address for f01
|
||||||
|
* @f01_data_base_addr: data base address for f01
|
||||||
|
* @irq: attention interrupt
|
||||||
|
* @sensor_max_x: sensor maximum x value
|
||||||
|
* @sensor_max_y: sensor maximum y value
|
||||||
|
* @irq_enabled: flag for indicating interrupt enable status
|
||||||
|
* @fingers_on_2d: flag to indicate presence of fingers in 2d area
|
||||||
|
* @sensor_sleep: flag to indicate sleep state of sensor
|
||||||
|
* @wait: wait queue for touch data polling in interrupt thread
|
||||||
|
* @irq_enable: pointer to irq enable function
|
||||||
|
*/
|
||||||
|
struct synaptics_rmi4_data {
|
||||||
|
struct platform_device *pdev;
|
||||||
|
struct input_dev *input_dev;
|
||||||
|
const struct synaptics_dsx_hw_interface *hw_if;
|
||||||
|
struct synaptics_rmi4_device_info rmi4_mod_info;
|
||||||
|
struct regulator *regulator;
|
||||||
|
struct mutex rmi4_reset_mutex;
|
||||||
|
struct mutex rmi4_io_ctrl_mutex;
|
||||||
|
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||||
|
struct early_suspend early_suspend;
|
||||||
|
#endif
|
||||||
|
unsigned char current_page;
|
||||||
|
unsigned char button_0d_enabled;
|
||||||
|
unsigned char full_pm_cycle;
|
||||||
|
unsigned char num_of_rx;
|
||||||
|
unsigned char num_of_tx;
|
||||||
|
unsigned char num_of_fingers;
|
||||||
|
unsigned char max_touch_width;
|
||||||
|
unsigned char report_enable;
|
||||||
|
unsigned char no_sleep_setting;
|
||||||
|
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;
|
||||||
|
unsigned int firmware_id;
|
||||||
|
int irq;
|
||||||
|
int sensor_max_x;
|
||||||
|
int sensor_max_y;
|
||||||
|
bool flash_prog_mode;
|
||||||
|
bool irq_enabled;
|
||||||
|
bool touch_stopped;
|
||||||
|
bool fingers_on_2d;
|
||||||
|
bool sensor_sleep;
|
||||||
|
bool stay_awake;
|
||||||
|
bool staying_awake;
|
||||||
|
int (*irq_enable)(struct synaptics_rmi4_data *rmi4_data, bool enable);
|
||||||
|
int (*reset_device)(struct synaptics_rmi4_data *rmi4_data);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct synaptics_dsx_bus_access {
|
||||||
|
unsigned char type;
|
||||||
|
int (*read)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr,
|
||||||
|
unsigned char *data, unsigned short length);
|
||||||
|
int (*write)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr,
|
||||||
|
unsigned char *data, unsigned short length);
|
||||||
|
};
|
||||||
|
|
||||||
|
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(unsigned char *fw_data);
|
||||||
|
|
||||||
|
static inline int synaptics_rmi4_reg_read(
|
||||||
|
struct synaptics_rmi4_data *rmi4_data,
|
||||||
|
unsigned short addr,
|
||||||
|
unsigned char *data,
|
||||||
|
unsigned short 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 short 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 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
|
1850
drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c
Executable file
1850
drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c
Executable file
File diff suppressed because it is too large
Load diff
271
drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c
Executable file
271
drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c
Executable file
|
@ -0,0 +1,271 @@
|
||||||
|
/*
|
||||||
|
* Synaptics DSX touchscreen driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Synaptics Incorporated
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#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/platform_device.h>
|
||||||
|
#include <linux/input/synaptics_dsx.h>
|
||||||
|
#include "synaptics_dsx_core.h"
|
||||||
|
|
||||||
|
#define SYN_I2C_RETRY_TIMES 10
|
||||||
|
|
||||||
|
static int synaptics_rmi4_i2c_set_page(struct synaptics_rmi4_data *rmi4_data,
|
||||||
|
unsigned short addr)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
unsigned char retry;
|
||||||
|
unsigned char buf[PAGE_SELECT_LEN];
|
||||||
|
unsigned char page;
|
||||||
|
struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
|
||||||
|
|
||||||
|
page = ((addr >> 8) & MASK_8BIT);
|
||||||
|
if (page != rmi4_data->current_page) {
|
||||||
|
buf[0] = MASK_8BIT;
|
||||||
|
buf[1] = page;
|
||||||
|
for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
|
||||||
|
retval = i2c_master_send(i2c, buf, PAGE_SELECT_LEN);
|
||||||
|
if (retval != PAGE_SELECT_LEN) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: I2C retry %d\n",
|
||||||
|
__func__, retry + 1);
|
||||||
|
msleep(20);
|
||||||
|
} else {
|
||||||
|
rmi4_data->current_page = page;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} 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 short length)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
unsigned char retry;
|
||||||
|
unsigned char buf;
|
||||||
|
struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
|
||||||
|
struct i2c_msg msg[] = {
|
||||||
|
{
|
||||||
|
.addr = i2c->addr,
|
||||||
|
.flags = 0,
|
||||||
|
.len = 1,
|
||||||
|
.buf = &buf,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.addr = i2c->addr,
|
||||||
|
.flags = I2C_M_RD,
|
||||||
|
.len = length,
|
||||||
|
.buf = data,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
buf = addr & MASK_8BIT;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
|
||||||
|
if (i2c_transfer(i2c->adapter, msg, 2) == 2) {
|
||||||
|
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) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: I2C read over retry limit\n",
|
||||||
|
__func__);
|
||||||
|
retval = -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 short length)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
unsigned char retry;
|
||||||
|
unsigned char buf[length + 1];
|
||||||
|
struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
|
||||||
|
struct i2c_msg msg[] = {
|
||||||
|
{
|
||||||
|
.addr = i2c->addr,
|
||||||
|
.flags = 0,
|
||||||
|
.len = length + 1,
|
||||||
|
.buf = buf,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[0] = addr & MASK_8BIT;
|
||||||
|
memcpy(&buf[1], &data[0], length);
|
||||||
|
|
||||||
|
for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
|
||||||
|
if (i2c_transfer(i2c->adapter, msg, 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) {
|
||||||
|
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 struct synaptics_dsx_hw_interface hw_if;
|
||||||
|
|
||||||
|
static struct platform_device *synaptics_dsx_i2c_device;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
hw_if.board_data = client->dev.platform_data;
|
||||||
|
hw_if.bus_access = &bus_access;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
static struct i2c_driver synaptics_rmi4_i2c_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = I2C_DRIVER_NAME,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
.probe = synaptics_rmi4_i2c_probe,
|
||||||
|
.remove = __devexit_p(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)
|
||||||
|
{
|
||||||
|
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");
|
671
drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_proximity.c
Executable file
671
drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_proximity.c
Executable file
|
@ -0,0 +1,671 @@
|
||||||
|
/*
|
||||||
|
* Synaptics DSX touchscreen driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Synaptics Incorporated
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#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/input1"
|
||||||
|
|
||||||
|
#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, (S_IRUGO | S_IWUGO),
|
||||||
|
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 & MASK_3BIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 & MASK_3BIT) +
|
||||||
|
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 (sscanf(buf, "%x", &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;
|
||||||
|
|
||||||
|
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 = PLATFORM_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)
|
||||||
|
return;
|
||||||
|
|
||||||
|
prox_hover_finger_lift();
|
||||||
|
|
||||||
|
prox_scan_pdt();
|
||||||
|
|
||||||
|
prox_set_hover_finger_en();
|
||||||
|
|
||||||
|
prox_set_params();
|
||||||
|
|
||||||
|
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");
|
781
drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c
Executable file
781
drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c
Executable file
|
@ -0,0 +1,781 @@
|
||||||
|
/*
|
||||||
|
* Synaptics DSX touchscreen driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Synaptics Incorporated
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#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/gpio.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/cdev.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/input/synaptics_dsx.h>
|
||||||
|
#include "synaptics_dsx_core.h"
|
||||||
|
|
||||||
|
#define CHAR_DEVICE_NAME "rmi"
|
||||||
|
#define DEVICE_CLASS_NAME "rmidev"
|
||||||
|
#define SYSFS_FOLDER_NAME "rmidev"
|
||||||
|
#define DEV_NUMBER 1
|
||||||
|
#define REG_ADDR_LIMIT 0xFFFF
|
||||||
|
|
||||||
|
static ssize_t rmidev_sysfs_data_show(struct file *data_file,
|
||||||
|
struct kobject *kobj, struct bin_attribute *attributes,
|
||||||
|
char *buf, loff_t pos, size_t count);
|
||||||
|
|
||||||
|
static ssize_t rmidev_sysfs_data_store(struct file *data_file,
|
||||||
|
struct kobject *kobj, struct bin_attribute *attributes,
|
||||||
|
char *buf, loff_t pos, size_t count);
|
||||||
|
|
||||||
|
static ssize_t rmidev_sysfs_open_store(struct device *dev,
|
||||||
|
struct device_attribute *attr, const char *buf, size_t count);
|
||||||
|
|
||||||
|
static ssize_t rmidev_sysfs_release_store(struct device *dev,
|
||||||
|
struct device_attribute *attr, const char *buf, size_t count);
|
||||||
|
|
||||||
|
static ssize_t rmidev_sysfs_attn_state_show(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf);
|
||||||
|
|
||||||
|
struct rmidev_handle {
|
||||||
|
dev_t dev_no;
|
||||||
|
struct device dev;
|
||||||
|
struct synaptics_rmi4_data *rmi4_data;
|
||||||
|
struct kobject *sysfs_dir;
|
||||||
|
void *data;
|
||||||
|
bool irq_enabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rmidev_data {
|
||||||
|
int ref_count;
|
||||||
|
struct cdev main_dev;
|
||||||
|
struct class *device_class;
|
||||||
|
struct mutex file_mutex;
|
||||||
|
struct rmidev_handle *rmi_dev;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct bin_attribute attr_data = {
|
||||||
|
.attr = {
|
||||||
|
.name = "data",
|
||||||
|
.mode = (S_IRUGO | S_IWUGO),
|
||||||
|
},
|
||||||
|
.size = 0,
|
||||||
|
.read = rmidev_sysfs_data_show,
|
||||||
|
.write = rmidev_sysfs_data_store,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct device_attribute attrs[] = {
|
||||||
|
__ATTR(open, S_IWUGO,
|
||||||
|
synaptics_rmi4_show_error,
|
||||||
|
rmidev_sysfs_open_store),
|
||||||
|
__ATTR(release, S_IWUGO,
|
||||||
|
synaptics_rmi4_show_error,
|
||||||
|
rmidev_sysfs_release_store),
|
||||||
|
__ATTR(attn_state, S_IRUGO,
|
||||||
|
rmidev_sysfs_attn_state_show,
|
||||||
|
synaptics_rmi4_store_error),
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rmidev_major_num;
|
||||||
|
|
||||||
|
static struct class *rmidev_device_class;
|
||||||
|
|
||||||
|
static struct rmidev_handle *rmidev;
|
||||||
|
|
||||||
|
DECLARE_COMPLETION(rmidev_remove_complete);
|
||||||
|
|
||||||
|
static irqreturn_t rmidev_sysfs_irq(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct synaptics_rmi4_data *rmi4_data = data;
|
||||||
|
|
||||||
|
sysfs_notify(&rmi4_data->input_dev->dev.kobj,
|
||||||
|
SYSFS_FOLDER_NAME, "attn_state");
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmidev_sysfs_irq_enable(struct synaptics_rmi4_data *rmi4_data,
|
||||||
|
bool enable)
|
||||||
|
{
|
||||||
|
int retval = 0;
|
||||||
|
unsigned char intr_status[MAX_INTR_REGISTERS];
|
||||||
|
unsigned long irq_flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
if (rmidev->irq_enabled)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
/* Clear interrupts first */
|
||||||
|
retval = synaptics_rmi4_reg_read(rmi4_data,
|
||||||
|
rmi4_data->f01_data_base_addr + 1,
|
||||||
|
intr_status,
|
||||||
|
rmi4_data->num_of_intr_regs);
|
||||||
|
if (retval < 0)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
retval = request_threaded_irq(rmi4_data->irq, NULL,
|
||||||
|
rmidev_sysfs_irq, irq_flags,
|
||||||
|
"synaptics_dsx_rmidev", rmi4_data);
|
||||||
|
if (retval < 0) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Failed to create irq thread\n",
|
||||||
|
__func__);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
rmidev->irq_enabled = true;
|
||||||
|
} else {
|
||||||
|
if (rmidev->irq_enabled) {
|
||||||
|
disable_irq(rmi4_data->irq);
|
||||||
|
free_irq(rmi4_data->irq, rmi4_data);
|
||||||
|
rmidev->irq_enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t rmidev_sysfs_data_show(struct file *data_file,
|
||||||
|
struct kobject *kobj, struct bin_attribute *attributes,
|
||||||
|
char *buf, loff_t pos, size_t count)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
unsigned int length = (unsigned int)count;
|
||||||
|
unsigned short address = (unsigned short)pos;
|
||||||
|
struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
|
||||||
|
|
||||||
|
if (length > (REG_ADDR_LIMIT - address)) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Out of register map limit\n",
|
||||||
|
__func__);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length) {
|
||||||
|
retval = synaptics_rmi4_reg_read(rmi4_data,
|
||||||
|
address,
|
||||||
|
(unsigned char *)buf,
|
||||||
|
length);
|
||||||
|
if (retval < 0) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Failed to read data\n",
|
||||||
|
__func__);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t rmidev_sysfs_data_store(struct file *data_file,
|
||||||
|
struct kobject *kobj, struct bin_attribute *attributes,
|
||||||
|
char *buf, loff_t pos, size_t count)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
unsigned int length = (unsigned int)count;
|
||||||
|
unsigned short address = (unsigned short)pos;
|
||||||
|
struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
|
||||||
|
|
||||||
|
if (length > (REG_ADDR_LIMIT - address)) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Out of register map limit\n",
|
||||||
|
__func__);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length) {
|
||||||
|
retval = synaptics_rmi4_reg_write(rmi4_data,
|
||||||
|
address,
|
||||||
|
(unsigned char *)buf,
|
||||||
|
length);
|
||||||
|
if (retval < 0) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Failed to write data\n",
|
||||||
|
__func__);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t rmidev_sysfs_open_store(struct device *dev,
|
||||||
|
struct device_attribute *attr, const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
unsigned int input;
|
||||||
|
struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
|
||||||
|
|
||||||
|
if (sscanf(buf, "%u", &input) != 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (input != 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
rmi4_data->irq_enable(rmi4_data, false);
|
||||||
|
rmidev_sysfs_irq_enable(rmi4_data, true);
|
||||||
|
|
||||||
|
dev_dbg(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Attention interrupt disabled\n",
|
||||||
|
__func__);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t rmidev_sysfs_release_store(struct device *dev,
|
||||||
|
struct device_attribute *attr, const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
unsigned int input;
|
||||||
|
struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
|
||||||
|
|
||||||
|
if (sscanf(buf, "%u", &input) != 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (input != 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
rmi4_data->reset_device(rmi4_data);
|
||||||
|
|
||||||
|
rmidev_sysfs_irq_enable(rmi4_data, false);
|
||||||
|
rmi4_data->irq_enable(rmi4_data, true);
|
||||||
|
|
||||||
|
dev_dbg(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Attention interrupt enabled\n",
|
||||||
|
__func__);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t rmidev_sysfs_attn_state_show(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
int attn_state;
|
||||||
|
struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
|
||||||
|
const struct synaptics_dsx_board_data *bdata =
|
||||||
|
rmi4_data->hw_if->board_data;
|
||||||
|
|
||||||
|
attn_state = gpio_get_value(bdata->irq_gpio);
|
||||||
|
|
||||||
|
return snprintf(buf, PAGE_SIZE, "%u\n", attn_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rmidev_llseek - used to set up register address
|
||||||
|
*
|
||||||
|
* @filp: file structure for seek
|
||||||
|
* @off: offset
|
||||||
|
* if whence == SEEK_SET,
|
||||||
|
* high 16 bits: page address
|
||||||
|
* low 16 bits: register address
|
||||||
|
* if whence == SEEK_CUR,
|
||||||
|
* offset from current position
|
||||||
|
* if whence == SEEK_END,
|
||||||
|
* offset from end position (0xFFFF)
|
||||||
|
* @whence: SEEK_SET, SEEK_CUR, or SEEK_END
|
||||||
|
*/
|
||||||
|
static loff_t rmidev_llseek(struct file *filp, loff_t off, int whence)
|
||||||
|
{
|
||||||
|
loff_t newpos;
|
||||||
|
struct rmidev_data *dev_data = filp->private_data;
|
||||||
|
struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
|
||||||
|
|
||||||
|
if (IS_ERR(dev_data)) {
|
||||||
|
pr_err("%s: Pointer of char device data is invalid", __func__);
|
||||||
|
return -EBADF;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&(dev_data->file_mutex));
|
||||||
|
|
||||||
|
switch (whence) {
|
||||||
|
case SEEK_SET:
|
||||||
|
newpos = off;
|
||||||
|
break;
|
||||||
|
case SEEK_CUR:
|
||||||
|
newpos = filp->f_pos + off;
|
||||||
|
break;
|
||||||
|
case SEEK_END:
|
||||||
|
newpos = REG_ADDR_LIMIT + off;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
newpos = -EINVAL;
|
||||||
|
goto clean_up;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newpos < 0 || newpos > REG_ADDR_LIMIT) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: New position 0x%04x is invalid\n",
|
||||||
|
__func__, (unsigned int)newpos);
|
||||||
|
newpos = -EINVAL;
|
||||||
|
goto clean_up;
|
||||||
|
}
|
||||||
|
|
||||||
|
filp->f_pos = newpos;
|
||||||
|
|
||||||
|
clean_up:
|
||||||
|
mutex_unlock(&(dev_data->file_mutex));
|
||||||
|
|
||||||
|
return newpos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rmidev_read: - use to read data from rmi device
|
||||||
|
*
|
||||||
|
* @filp: file structure for read
|
||||||
|
* @buf: user space buffer pointer
|
||||||
|
* @count: number of bytes to read
|
||||||
|
* @f_pos: offset (starting register address)
|
||||||
|
*/
|
||||||
|
static ssize_t rmidev_read(struct file *filp, char __user *buf,
|
||||||
|
size_t count, loff_t *f_pos)
|
||||||
|
{
|
||||||
|
ssize_t retval;
|
||||||
|
unsigned char tmpbuf[count + 1];
|
||||||
|
struct rmidev_data *dev_data = filp->private_data;
|
||||||
|
|
||||||
|
if (IS_ERR(dev_data)) {
|
||||||
|
pr_err("%s: Pointer of char device data is invalid", __func__);
|
||||||
|
return -EBADF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (count > (REG_ADDR_LIMIT - *f_pos))
|
||||||
|
count = REG_ADDR_LIMIT - *f_pos;
|
||||||
|
|
||||||
|
mutex_lock(&(dev_data->file_mutex));
|
||||||
|
|
||||||
|
retval = synaptics_rmi4_reg_read(rmidev->rmi4_data,
|
||||||
|
*f_pos,
|
||||||
|
tmpbuf,
|
||||||
|
count);
|
||||||
|
if (retval < 0)
|
||||||
|
goto clean_up;
|
||||||
|
|
||||||
|
if (copy_to_user(buf, tmpbuf, count))
|
||||||
|
retval = -EFAULT;
|
||||||
|
else
|
||||||
|
*f_pos += retval;
|
||||||
|
|
||||||
|
clean_up:
|
||||||
|
mutex_unlock(&(dev_data->file_mutex));
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rmidev_write: - used to write data to rmi device
|
||||||
|
*
|
||||||
|
* @filep: file structure for write
|
||||||
|
* @buf: user space buffer pointer
|
||||||
|
* @count: number of bytes to write
|
||||||
|
* @f_pos: offset (starting register address)
|
||||||
|
*/
|
||||||
|
static ssize_t rmidev_write(struct file *filp, const char __user *buf,
|
||||||
|
size_t count, loff_t *f_pos)
|
||||||
|
{
|
||||||
|
ssize_t retval;
|
||||||
|
unsigned char tmpbuf[count + 1];
|
||||||
|
struct rmidev_data *dev_data = filp->private_data;
|
||||||
|
|
||||||
|
if (IS_ERR(dev_data)) {
|
||||||
|
pr_err("%s: Pointer of char device data is invalid", __func__);
|
||||||
|
return -EBADF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (count > (REG_ADDR_LIMIT - *f_pos))
|
||||||
|
count = REG_ADDR_LIMIT - *f_pos;
|
||||||
|
|
||||||
|
if (copy_from_user(tmpbuf, buf, count))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
mutex_lock(&(dev_data->file_mutex));
|
||||||
|
|
||||||
|
retval = synaptics_rmi4_reg_write(rmidev->rmi4_data,
|
||||||
|
*f_pos,
|
||||||
|
tmpbuf,
|
||||||
|
count);
|
||||||
|
if (retval >= 0)
|
||||||
|
*f_pos += retval;
|
||||||
|
|
||||||
|
mutex_unlock(&(dev_data->file_mutex));
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rmidev_open: enable access to rmi device
|
||||||
|
* @inp: inode struture
|
||||||
|
* @filp: file structure
|
||||||
|
*/
|
||||||
|
static int rmidev_open(struct inode *inp, struct file *filp)
|
||||||
|
{
|
||||||
|
int retval = 0;
|
||||||
|
struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
|
||||||
|
struct rmidev_data *dev_data =
|
||||||
|
container_of(inp->i_cdev, struct rmidev_data, main_dev);
|
||||||
|
|
||||||
|
if (!dev_data)
|
||||||
|
return -EACCES;
|
||||||
|
|
||||||
|
filp->private_data = dev_data;
|
||||||
|
|
||||||
|
mutex_lock(&(dev_data->file_mutex));
|
||||||
|
|
||||||
|
rmi4_data->irq_enable(rmi4_data, false);
|
||||||
|
dev_dbg(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Attention interrupt disabled\n",
|
||||||
|
__func__);
|
||||||
|
|
||||||
|
if (dev_data->ref_count < 1)
|
||||||
|
dev_data->ref_count++;
|
||||||
|
else
|
||||||
|
retval = -EACCES;
|
||||||
|
|
||||||
|
mutex_unlock(&(dev_data->file_mutex));
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rmidev_release: - release access to rmi device
|
||||||
|
* @inp: inode structure
|
||||||
|
* @filp: file structure
|
||||||
|
*/
|
||||||
|
static int rmidev_release(struct inode *inp, struct file *filp)
|
||||||
|
{
|
||||||
|
struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
|
||||||
|
struct rmidev_data *dev_data =
|
||||||
|
container_of(inp->i_cdev, struct rmidev_data, main_dev);
|
||||||
|
|
||||||
|
if (!dev_data)
|
||||||
|
return -EACCES;
|
||||||
|
|
||||||
|
rmi4_data->reset_device(rmi4_data);
|
||||||
|
|
||||||
|
mutex_lock(&(dev_data->file_mutex));
|
||||||
|
|
||||||
|
dev_data->ref_count--;
|
||||||
|
if (dev_data->ref_count < 0)
|
||||||
|
dev_data->ref_count = 0;
|
||||||
|
|
||||||
|
rmi4_data->irq_enable(rmi4_data, true);
|
||||||
|
dev_dbg(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Attention interrupt enabled\n",
|
||||||
|
__func__);
|
||||||
|
|
||||||
|
mutex_unlock(&(dev_data->file_mutex));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations rmidev_fops = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.llseek = rmidev_llseek,
|
||||||
|
.read = rmidev_read,
|
||||||
|
.write = rmidev_write,
|
||||||
|
.open = rmidev_open,
|
||||||
|
.release = rmidev_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void rmidev_device_cleanup(struct rmidev_data *dev_data)
|
||||||
|
{
|
||||||
|
dev_t devno;
|
||||||
|
struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
|
||||||
|
|
||||||
|
if (dev_data) {
|
||||||
|
devno = dev_data->main_dev.dev;
|
||||||
|
|
||||||
|
if (dev_data->device_class)
|
||||||
|
device_destroy(dev_data->device_class, devno);
|
||||||
|
|
||||||
|
cdev_del(&dev_data->main_dev);
|
||||||
|
|
||||||
|
unregister_chrdev_region(devno, 1);
|
||||||
|
|
||||||
|
dev_dbg(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: rmidev device removed\n",
|
||||||
|
__func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *rmi_char_devnode(struct device *dev, mode_t *mode)
|
||||||
|
{
|
||||||
|
if (!mode)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
*mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||||
|
|
||||||
|
return kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmidev_create_device_class(void)
|
||||||
|
{
|
||||||
|
rmidev_device_class = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
|
||||||
|
|
||||||
|
if (IS_ERR(rmidev_device_class)) {
|
||||||
|
pr_err("%s: Failed to create /dev/%s\n",
|
||||||
|
__func__, CHAR_DEVICE_NAME);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
rmidev_device_class->devnode = rmi_char_devnode;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmidev_init_device(struct synaptics_rmi4_data *rmi4_data)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
dev_t dev_no;
|
||||||
|
unsigned char attr_count;
|
||||||
|
struct rmidev_data *dev_data;
|
||||||
|
struct device *device_ptr;
|
||||||
|
const struct synaptics_dsx_board_data *bdata =
|
||||||
|
rmi4_data->hw_if->board_data;
|
||||||
|
|
||||||
|
rmidev = kzalloc(sizeof(*rmidev), GFP_KERNEL);
|
||||||
|
if (!rmidev) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Failed to alloc mem for rmidev\n",
|
||||||
|
__func__);
|
||||||
|
retval = -ENOMEM;
|
||||||
|
goto err_rmidev;
|
||||||
|
}
|
||||||
|
|
||||||
|
rmidev->rmi4_data = rmi4_data;
|
||||||
|
|
||||||
|
retval = rmidev_create_device_class();
|
||||||
|
if (retval < 0) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Failed to create device class\n",
|
||||||
|
__func__);
|
||||||
|
goto err_device_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rmidev_major_num) {
|
||||||
|
dev_no = MKDEV(rmidev_major_num, DEV_NUMBER);
|
||||||
|
retval = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME);
|
||||||
|
} else {
|
||||||
|
retval = alloc_chrdev_region(&dev_no, 0, 1, CHAR_DEVICE_NAME);
|
||||||
|
if (retval < 0) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Failed to allocate char device region\n",
|
||||||
|
__func__);
|
||||||
|
goto err_device_region;
|
||||||
|
}
|
||||||
|
|
||||||
|
rmidev_major_num = MAJOR(dev_no);
|
||||||
|
dev_dbg(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Major number of rmidev = %d\n",
|
||||||
|
__func__, rmidev_major_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL);
|
||||||
|
if (!dev_data) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Failed to alloc mem for dev_data\n",
|
||||||
|
__func__);
|
||||||
|
retval = -ENOMEM;
|
||||||
|
goto err_dev_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_init(&dev_data->file_mutex);
|
||||||
|
dev_data->rmi_dev = rmidev;
|
||||||
|
rmidev->data = dev_data;
|
||||||
|
|
||||||
|
cdev_init(&dev_data->main_dev, &rmidev_fops);
|
||||||
|
|
||||||
|
retval = cdev_add(&dev_data->main_dev, dev_no, 1);
|
||||||
|
if (retval < 0) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Failed to add rmi char device\n",
|
||||||
|
__func__);
|
||||||
|
goto err_char_device;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_set_name(&rmidev->dev, "rmidev%d", MINOR(dev_no));
|
||||||
|
dev_data->device_class = rmidev_device_class;
|
||||||
|
|
||||||
|
device_ptr = device_create(dev_data->device_class, NULL, dev_no,
|
||||||
|
NULL, CHAR_DEVICE_NAME"%d", MINOR(dev_no));
|
||||||
|
if (IS_ERR(device_ptr)) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Failed to create rmi char device\n",
|
||||||
|
__func__);
|
||||||
|
retval = -ENODEV;
|
||||||
|
goto err_char_device;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = gpio_export(bdata->irq_gpio, false);
|
||||||
|
if (retval < 0) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Failed to export attention gpio\n",
|
||||||
|
__func__);
|
||||||
|
} else {
|
||||||
|
retval = gpio_export_link(&(rmi4_data->input_dev->dev),
|
||||||
|
"attn", bdata->irq_gpio);
|
||||||
|
if (retval < 0) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s Failed to create gpio symlink\n",
|
||||||
|
__func__);
|
||||||
|
} else {
|
||||||
|
dev_dbg(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Exported attention gpio %d\n",
|
||||||
|
__func__, bdata->irq_gpio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rmidev->sysfs_dir = kobject_create_and_add(SYSFS_FOLDER_NAME,
|
||||||
|
&rmi4_data->input_dev->dev.kobj);
|
||||||
|
if (!rmidev->sysfs_dir) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Failed to create sysfs directory\n",
|
||||||
|
__func__);
|
||||||
|
retval = -ENODEV;
|
||||||
|
goto err_sysfs_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = sysfs_create_bin_file(rmidev->sysfs_dir,
|
||||||
|
&attr_data);
|
||||||
|
if (retval < 0) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Failed to create sysfs bin file\n",
|
||||||
|
__func__);
|
||||||
|
goto err_sysfs_bin;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
|
||||||
|
retval = sysfs_create_file(rmidev->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 err_sysfs_attrs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_sysfs_attrs:
|
||||||
|
for (attr_count--; attr_count >= 0; attr_count--)
|
||||||
|
sysfs_remove_file(rmidev->sysfs_dir, &attrs[attr_count].attr);
|
||||||
|
|
||||||
|
sysfs_remove_bin_file(rmidev->sysfs_dir, &attr_data);
|
||||||
|
|
||||||
|
err_sysfs_bin:
|
||||||
|
kobject_put(rmidev->sysfs_dir);
|
||||||
|
|
||||||
|
err_sysfs_dir:
|
||||||
|
err_char_device:
|
||||||
|
rmidev_device_cleanup(dev_data);
|
||||||
|
kfree(dev_data);
|
||||||
|
|
||||||
|
err_dev_data:
|
||||||
|
unregister_chrdev_region(dev_no, 1);
|
||||||
|
|
||||||
|
err_device_region:
|
||||||
|
class_destroy(rmidev_device_class);
|
||||||
|
|
||||||
|
err_device_class:
|
||||||
|
kfree(rmidev);
|
||||||
|
rmidev = NULL;
|
||||||
|
|
||||||
|
err_rmidev:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rmidev_remove_device(struct synaptics_rmi4_data *rmi4_data)
|
||||||
|
{
|
||||||
|
unsigned char attr_count;
|
||||||
|
struct rmidev_data *dev_data;
|
||||||
|
|
||||||
|
if (!rmidev)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
|
||||||
|
sysfs_remove_file(rmidev->sysfs_dir, &attrs[attr_count].attr);
|
||||||
|
|
||||||
|
sysfs_remove_bin_file(rmidev->sysfs_dir, &attr_data);
|
||||||
|
|
||||||
|
kobject_put(rmidev->sysfs_dir);
|
||||||
|
|
||||||
|
dev_data = rmidev->data;
|
||||||
|
if (dev_data) {
|
||||||
|
rmidev_device_cleanup(dev_data);
|
||||||
|
kfree(dev_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
unregister_chrdev_region(rmidev->dev_no, 1);
|
||||||
|
|
||||||
|
class_destroy(rmidev_device_class);
|
||||||
|
|
||||||
|
kfree(rmidev);
|
||||||
|
rmidev = NULL;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
complete(&rmidev_remove_complete);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct synaptics_rmi4_exp_fn rmidev_module = {
|
||||||
|
.fn_type = RMI_DEV,
|
||||||
|
.init = rmidev_init_device,
|
||||||
|
.remove = rmidev_remove_device,
|
||||||
|
.reset = NULL,
|
||||||
|
.reinit = NULL,
|
||||||
|
.early_suspend = NULL,
|
||||||
|
.suspend = NULL,
|
||||||
|
.resume = NULL,
|
||||||
|
.late_resume = NULL,
|
||||||
|
.attn = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init rmidev_module_init(void)
|
||||||
|
{
|
||||||
|
synaptics_rmi4_new_function(&rmidev_module, true);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit rmidev_module_exit(void)
|
||||||
|
{
|
||||||
|
synaptics_rmi4_new_function(&rmidev_module, false);
|
||||||
|
|
||||||
|
wait_for_completion(&rmidev_remove_complete);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(rmidev_module_init);
|
||||||
|
module_exit(rmidev_module_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Synaptics, Inc.");
|
||||||
|
MODULE_DESCRIPTION("Synaptics DSX RMI Dev Module");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
335
drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_spi.c
Executable file
335
drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_spi.c
Executable file
|
@ -0,0 +1,335 @@
|
||||||
|
/*
|
||||||
|
* Synaptics DSX touchscreen driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Synaptics Incorporated
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#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/platform_device.h>
|
||||||
|
#include <linux/input/synaptics_dsx.h>
|
||||||
|
#include "synaptics_dsx_core.h"
|
||||||
|
|
||||||
|
#define SPI_READ 0x80
|
||||||
|
#define SPI_WRITE 0x00
|
||||||
|
|
||||||
|
static int synaptics_rmi4_spi_set_page(struct synaptics_rmi4_data *rmi4_data,
|
||||||
|
unsigned short addr)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
unsigned int index;
|
||||||
|
unsigned int xfer_count = PAGE_SELECT_LEN + 1;
|
||||||
|
unsigned char txbuf[xfer_count];
|
||||||
|
unsigned char page;
|
||||||
|
struct spi_message msg;
|
||||||
|
struct spi_transfer xfers[xfer_count];
|
||||||
|
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_7BIT);
|
||||||
|
if (page != rmi4_data->current_page) {
|
||||||
|
spi_message_init(&msg);
|
||||||
|
|
||||||
|
txbuf[0] = SPI_WRITE;
|
||||||
|
txbuf[1] = MASK_8BIT;
|
||||||
|
txbuf[2] = page;
|
||||||
|
|
||||||
|
for (index = 0; index < xfer_count; index++) {
|
||||||
|
memset(&xfers[index], 0, sizeof(struct spi_transfer));
|
||||||
|
xfers[index].len = 1;
|
||||||
|
xfers[index].delay_usecs = bdata->byte_delay_us;
|
||||||
|
xfers[index].tx_buf = &txbuf[index];
|
||||||
|
spi_message_add_tail(&xfers[index], &msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bdata->block_delay_us)
|
||||||
|
xfers[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);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
retval = PAGE_SELECT_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int synaptics_rmi4_spi_read(struct synaptics_rmi4_data *rmi4_data,
|
||||||
|
unsigned short addr, unsigned char *data, unsigned short length)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
unsigned int index;
|
||||||
|
unsigned int xfer_count = length + ADDRESS_WORD_LEN;
|
||||||
|
unsigned char txbuf[ADDRESS_WORD_LEN];
|
||||||
|
unsigned char *rxbuf = NULL;
|
||||||
|
struct spi_message msg;
|
||||||
|
struct spi_transfer *xfers = NULL;
|
||||||
|
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);
|
||||||
|
|
||||||
|
xfers = kcalloc(xfer_count, sizeof(struct spi_transfer), GFP_KERNEL);
|
||||||
|
if (!xfers) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Failed to allocate memory for xfers\n",
|
||||||
|
__func__);
|
||||||
|
retval = -ENOMEM;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
txbuf[0] = (addr >> 8) | SPI_READ;
|
||||||
|
txbuf[1] = addr & MASK_8BIT;
|
||||||
|
|
||||||
|
rxbuf = kmalloc(length, GFP_KERNEL);
|
||||||
|
if (!rxbuf) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Failed to allocate memory for rxbuf\n",
|
||||||
|
__func__);
|
||||||
|
retval = -ENOMEM;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
|
||||||
|
|
||||||
|
retval = synaptics_rmi4_spi_set_page(rmi4_data, addr);
|
||||||
|
if (retval != PAGE_SELECT_LEN) {
|
||||||
|
retval = -EIO;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (index = 0; index < xfer_count; index++) {
|
||||||
|
xfers[index].len = 1;
|
||||||
|
xfers[index].delay_usecs = bdata->byte_delay_us;
|
||||||
|
if (index < ADDRESS_WORD_LEN)
|
||||||
|
xfers[index].tx_buf = &txbuf[index];
|
||||||
|
else
|
||||||
|
xfers[index].rx_buf = &rxbuf[index - ADDRESS_WORD_LEN];
|
||||||
|
spi_message_add_tail(&xfers[index], &msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bdata->block_delay_us)
|
||||||
|
xfers[index - 1].delay_usecs = bdata->block_delay_us;
|
||||||
|
|
||||||
|
retval = spi_sync(spi, &msg);
|
||||||
|
if (retval == 0) {
|
||||||
|
retval = length;
|
||||||
|
memcpy(data, rxbuf, 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);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
kfree(rxbuf);
|
||||||
|
kfree(xfers);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int synaptics_rmi4_spi_write(struct synaptics_rmi4_data *rmi4_data,
|
||||||
|
unsigned short addr, unsigned char *data, unsigned short length)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
unsigned int index;
|
||||||
|
unsigned int xfer_count = length + ADDRESS_WORD_LEN;
|
||||||
|
unsigned char *txbuf = NULL;
|
||||||
|
struct spi_message msg;
|
||||||
|
struct spi_transfer *xfers = NULL;
|
||||||
|
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);
|
||||||
|
|
||||||
|
xfers = kcalloc(xfer_count, sizeof(struct spi_transfer), GFP_KERNEL);
|
||||||
|
if (!xfers) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Failed to allocate memory for xfers\n",
|
||||||
|
__func__);
|
||||||
|
retval = -ENOMEM;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
txbuf = kmalloc(xfer_count, GFP_KERNEL);
|
||||||
|
if (!txbuf) {
|
||||||
|
dev_err(rmi4_data->pdev->dev.parent,
|
||||||
|
"%s: Failed to allocate memory for txbuf\n",
|
||||||
|
__func__);
|
||||||
|
retval = -ENOMEM;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
txbuf[0] = (addr >> 8) & ~SPI_READ;
|
||||||
|
txbuf[1] = addr & MASK_8BIT;
|
||||||
|
memcpy(&txbuf[ADDRESS_WORD_LEN], data, length);
|
||||||
|
|
||||||
|
mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
|
||||||
|
|
||||||
|
retval = synaptics_rmi4_spi_set_page(rmi4_data, addr);
|
||||||
|
if (retval != PAGE_SELECT_LEN) {
|
||||||
|
retval = -EIO;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (index = 0; index < xfer_count; index++) {
|
||||||
|
xfers[index].len = 1;
|
||||||
|
xfers[index].delay_usecs = bdata->byte_delay_us;
|
||||||
|
xfers[index].tx_buf = &txbuf[index];
|
||||||
|
spi_message_add_tail(&xfers[index], &msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bdata->block_delay_us)
|
||||||
|
xfers[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);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
kfree(txbuf);
|
||||||
|
kfree(xfers);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
hw_if.board_data = spi->dev.platform_data;
|
||||||
|
hw_if.bus_access = &bus_access;
|
||||||
|
|
||||||
|
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 struct spi_driver synaptics_rmi4_spi_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = SPI_DRIVER_NAME,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
.probe = synaptics_rmi4_spi_probe,
|
||||||
|
.remove = __devexit_p(synaptics_rmi4_spi_remove),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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");
|
80
include/linux/input/synaptics_dsx_v2.h
Executable file
80
include/linux/input/synaptics_dsx_v2.h
Executable file
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* Synaptics DSX touchscreen driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Synaptics Incorporated
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SYNAPTICS_DSX_H_
|
||||||
|
#define _SYNAPTICS_DSX_H_
|
||||||
|
|
||||||
|
#define PLATFORM_DRIVER_NAME "synaptics_dsx"
|
||||||
|
#define I2C_DRIVER_NAME "synaptics_dsx_i2c"
|
||||||
|
#define SPI_DRIVER_NAME "synaptics_dsx_spi"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* struct synaptics_dsx_cap_button_map - 0d button map
|
||||||
|
* @nbuttons: number of 0d buttons
|
||||||
|
* @map: pointer to array of button types
|
||||||
|
*/
|
||||||
|
struct synaptics_dsx_cap_button_map {
|
||||||
|
unsigned char nbuttons;
|
||||||
|
unsigned char *map;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* struct synaptics_dsx_board_data - dsx board data
|
||||||
|
* @x_flip: x flip flag
|
||||||
|
* @y_flip: y flip flag
|
||||||
|
* @irq_gpio: attention interrupt gpio
|
||||||
|
* @power_gpio: power switch gpio
|
||||||
|
* @power_on_state: power switch active state
|
||||||
|
* @reset_gpio: reset gpio
|
||||||
|
* @reset_on_state: reset active state
|
||||||
|
* @irq_flags: irq flags
|
||||||
|
* @panel_x: x-axis resolution of display panel
|
||||||
|
* @panel_y: y-axis resolution of display panel
|
||||||
|
* @power_delay_ms: delay time to wait after power-on
|
||||||
|
* @reset_delay_ms: delay time to wait after reset
|
||||||
|
* @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
|
||||||
|
* @regulator_name: pointer to name of regulator
|
||||||
|
* @gpio_config: pointer to gpio configuration function
|
||||||
|
* @cap_button_map: pointer to 0d button map
|
||||||
|
*/
|
||||||
|
struct synaptics_dsx_board_data {
|
||||||
|
bool x_flip;
|
||||||
|
bool y_flip;
|
||||||
|
bool swap_axes;
|
||||||
|
int irq_gpio;
|
||||||
|
int power_gpio;
|
||||||
|
int power_on_state;
|
||||||
|
int reset_gpio;
|
||||||
|
int reset_on_state;
|
||||||
|
unsigned long irq_flags;
|
||||||
|
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 char *regulator_name;
|
||||||
|
int (*gpio_config)(int gpio, bool configure, int dir, int state);
|
||||||
|
struct synaptics_dsx_cap_button_map *cap_button_map;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Add table
Reference in a new issue