input: touchscreen: Import Synaptics ClearPad driver

* Sony package version: 52.1.A.0.532

Change-Id: Ie821d2ef530412b1c8aa40ed16e3b3091b5890c0
Signed-off-by: Sorayukii <sorayukii69@gmail.com>
This commit is contained in:
Olivier Karasangabo 2020-03-24 18:57:32 +01:00 • committed by Sorayukii
commit 24b7b040df
8 changed files with 10804 additions and 0 deletions

View file

@ -164,6 +164,26 @@ config TOUCHSCREEN_CHIPONE_ICN8318
To compile this driver as a module, choose M here: the
module will be called chipone_icn8318.
config TOUCHSCREEN_CLEARPAD
tristate "Synaptics ClearPad"
default n
help
Say Y here to enable Synaptics ClearPad touchscreen.
config TOUCHSCREEN_CLEARPAD_I2C
tristate "Synaptics ClearPad I2C"
depends on I2C && TOUCHSCREEN_CLEARPAD
default n
help
Say Y here to enable Synaptics ClearPad I2C touchscreen.
config TOUCHSCREEN_CLEARPAD_RMI_DEV
tristate "Synaptics ClearPad RMI Dev"
depends on GPIO_SYSFS && TOUCHSCREEN_CLEARPAD && TOUCHSCREEN_CLEARPAD_I2C
default n
help
Say Y here to enable Synaptics ClearPad RMI dev.
config TOUCHSCREEN_CY8CTMG110
tristate "cy8ctmg110 touchscreen"
depends on I2C

View file

@ -18,6 +18,9 @@ obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o
obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o
obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318) += chipone_icn8318.o
obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_RMI_DEV) += clearpad_rmi_dev.o
obj-$(CONFIG_TOUCHSCREEN_CLEARPAD) += clearpad_core.o
obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_I2C) += clearpad_i2c.o
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,335 @@
/* msm/clearpad/clearpad_i2c/clearpad_i2c.c
*
* Copyright (c) 2011 Synaptics Incorporated
* Copyright (c) 2011 Unixphere
* Copyright (C) 2017 Sony Mobile Communications Inc.
*
* Author: Yusuke Yoshimura <Yusuke.Yoshimura@sonyericsson.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*/
#include <linux/async.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/clearpad.h>
#define CLEARPAD_PAGE_SELECT_REGISTER 0xff
#define CLEARPAD_REG(addr) ((addr) & 0xff)
#define CLEARPAD_PAGE(addr) (((addr) >> 8) & 0xff)
struct clearpad_i2c_t {
struct platform_device *pdev;
unsigned int page;
struct mutex page_mutex;
};
static int clearpad_i2c_set_page(struct device *dev, u8 page)
{
struct clearpad_i2c_t *this = dev_get_drvdata(dev);
char txbuf[2] = {CLEARPAD_PAGE_SELECT_REGISTER, page};
int rc = 0;
rc = i2c_master_send(to_i2c_client(dev), txbuf, sizeof(txbuf));
if (rc != sizeof(txbuf)) {
dev_err(dev,
"%s: set page failed: %d.", __func__, rc);
rc = (rc < 0) ? rc : -EIO;
goto exit;
}
this->page = page;
rc = 0;
exit:
return rc;
}
static int clearpad_i2c_read(struct device *dev, u16 addr, u8 *buf, u8 len)
{
struct clearpad_i2c_t *this = dev_get_drvdata(dev);
s32 rc = 0;
u8 page = CLEARPAD_PAGE(addr);
u8 reg = CLEARPAD_REG(addr);
int rsize = I2C_SMBUS_BLOCK_MAX;
int off;
mutex_lock(&this->page_mutex);
if (page != this->page) {
rc = clearpad_i2c_set_page(dev, page);
if (rc < 0)
goto exit;
}
for (off = 0; off < len; off += rsize) {
if (len < off + I2C_SMBUS_BLOCK_MAX)
rsize = len - off;
rc = i2c_smbus_read_i2c_block_data(to_i2c_client(dev),
reg + off, rsize, &buf[off]);
if (rc != rsize) {
dev_err(dev, "%s: rc = %d\n", __func__, rc);
if (rc > 0) {
off += rc;
break;
}
goto exit;
}
}
rc = off;
exit:
mutex_unlock(&this->page_mutex);
return rc;
}
static int clearpad_i2c_write(struct device *dev, u16 addr,
const u8 *buf, u8 len)
{
struct clearpad_i2c_t *this = dev_get_drvdata(dev);
int rc = 0;
u8 reg = CLEARPAD_REG(addr);
u8 i;
mutex_lock(&this->page_mutex);
if (CLEARPAD_PAGE(addr) != this->page) {
rc = clearpad_i2c_set_page(dev, CLEARPAD_PAGE(addr));
if (rc < 0)
goto exit;
}
for (i = 0; i < len; i++) {
rc = i2c_smbus_write_byte_data(to_i2c_client(dev),
reg + i, buf[i]);
if (rc)
goto exit;
}
rc = i;
exit:
mutex_unlock(&this->page_mutex);
return rc;
}
static int clearpad_i2c_read_block(struct device *dev, u16 addr, u8 *buf,
int len)
{
struct clearpad_i2c_t *this = dev_get_drvdata(dev);
u8 txbuf[1] = {addr & 0xff};
int rc = 0;
mutex_lock(&this->page_mutex);
if (CLEARPAD_PAGE(addr) != this->page) {
rc = clearpad_i2c_set_page(dev, CLEARPAD_PAGE(addr));
if (rc < 0)
goto exit;
}
rc = i2c_master_send(to_i2c_client(dev), txbuf, sizeof(txbuf));
if (rc != sizeof(txbuf)) {
rc = (rc < 0) ? rc : -EIO;
goto exit;
}
rc = i2c_master_recv(to_i2c_client(dev), buf, len);
if (rc < 0)
dev_err(dev, "%s: rc = %d\n", __func__, rc);
exit:
mutex_unlock(&this->page_mutex);
return rc;
}
static int clearpad_i2c_write_block(struct device *dev, u16 addr, const u8 *buf,
int len)
{
struct clearpad_i2c_t *this = dev_get_drvdata(dev);
u8 txbuf[len + 1];
int rc = 0;
txbuf[0] = addr & 0xff;
memcpy(txbuf + 1, buf, len);
mutex_lock(&this->page_mutex);
if (CLEARPAD_PAGE(addr) != this->page) {
rc = clearpad_i2c_set_page(dev, CLEARPAD_PAGE(addr));
if (rc < 0)
goto exit;
}
rc = i2c_master_send(to_i2c_client(dev), txbuf, sizeof(txbuf));
if (rc < 0)
dev_err(dev, "%s: rc = %d\n", __func__, rc);
else
rc -= 1;
exit:
mutex_unlock(&this->page_mutex);
return rc;
}
static struct clearpad_bus_data_t clearpad_i2c_bus_data = {
.bustype = BUS_I2C,
.set_page = clearpad_i2c_set_page,
.read = clearpad_i2c_read,
.write = clearpad_i2c_write,
.read_block = clearpad_i2c_read_block,
.write_block = clearpad_i2c_write_block,
};
#ifdef CONFIG_OF
static int clearpad_parse_dt(struct device *dev,
struct clearpad_platform_data_t *pdata)
{
struct device_node *np = dev->of_node;
pdata->irq_gpio = of_get_named_gpio_flags(np, "synaptics,irq_gpio",
0, &pdata->irq_gpio_flags);
return 0;
}
#else
static int clearpad_parse_dt(struct device *dev,
struct clearpad_platform_data *pdata)
{
return -ENODEV;
}
#endif
static int clearpad_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct clearpad_data_t clearpad_data = {
.pdata = NULL,
.bdata = &clearpad_i2c_bus_data,
.probe_retry = 0,
.rmi_dev = NULL,
};
struct clearpad_i2c_t *this;
int rc;
if (client->dev.of_node) {
clearpad_data.pdata = devm_kzalloc(&client->dev,
sizeof(struct clearpad_platform_data_t),
GFP_KERNEL);
if (!clearpad_data.pdata) {
dev_err(&client->dev, "failed to allocate memory\n");
rc = -ENOMEM;
goto exit;
}
rc = clearpad_parse_dt(&client->dev, clearpad_data.pdata);
if (rc) {
dev_err(&client->dev, "failed to parse device tree\n");
goto exit;
}
} else {
clearpad_data.pdata = client->dev.platform_data;
}
this = kzalloc(sizeof(struct clearpad_i2c_t), GFP_KERNEL);
if (!this) {
rc = -ENOMEM;
goto exit;
}
dev_set_drvdata(&client->dev, this);
mutex_init(&this->page_mutex);
this->pdev = platform_device_alloc(CLEARPAD_NAME, -1);
if (!this->pdev) {
rc = -ENOMEM;
goto err_free;
}
clearpad_data.bdata->dev = &client->dev;
clearpad_data.bdata->of_node = client->dev.of_node;
this->pdev->dev.parent = &client->dev;
rc = platform_device_add_data(this->pdev,
&clearpad_data, sizeof(clearpad_data));
if (rc)
goto err_device_put;
rc = platform_device_add(this->pdev);
if (rc)
goto err_device_put;
dev_info(&client->dev, "%s: success\n", __func__);
goto exit;
err_device_put:
platform_device_put(this->pdev);
err_free:
dev_set_drvdata(&client->dev, NULL);
kfree(this);
exit:
return rc;
}
static int clearpad_i2c_remove(struct i2c_client *client)
{
struct clearpad_i2c_t *this = dev_get_drvdata(&client->dev);
platform_device_unregister(this->pdev);
dev_set_drvdata(&client->dev, NULL);
kfree(this);
return 0;
}
static const struct i2c_device_id clearpad_id[] = {
{ CLEARPADI2C_NAME, 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, clearpad_id);
#ifdef CONFIG_OF
static struct of_device_id clearpad_match_table[] = {
{ .compatible = "synaptics,clearpad", },
{ },
};
#else
#define clearpad_match_table NULL
#endif
static struct i2c_driver clearpad_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = CLEARPADI2C_NAME,
.of_match_table = clearpad_match_table,
},
.id_table = clearpad_id,
.probe = clearpad_i2c_probe,
.remove = clearpad_i2c_remove,
};
#ifndef MODULE
void clearpad_i2c_init_async(void *unused, async_cookie_t cookie)
{
int rc;
rc = i2c_add_driver(&clearpad_i2c_driver);
if (rc != 0)
pr_err("Clearpad I2C registration failed rc = %d\n", rc);
}
#endif
static int __init clearpad_i2c_init(void)
{
#ifdef MODULE
return i2c_add_driver(&clearpad_i2c_driver);
#else
async_schedule(clearpad_i2c_init_async, NULL);
return 0;
#endif
}
static void __exit clearpad_i2c_exit(void)
{
i2c_del_driver(&clearpad_i2c_driver);
}
MODULE_DESCRIPTION(CLEARPADI2C_NAME "ClearPad I2C Driver");
MODULE_LICENSE("GPL v2");
module_init(clearpad_i2c_init);
module_exit(clearpad_i2c_exit);

View file

@ -0,0 +1,512 @@
/*
* Copyright (c) 2011 Synaptics Incorporated
* Copyright (C) 2017 Sony Mobile Communications Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/syscalls.h>
#include <linux/clearpad.h>
#define CHAR_DEVICE_NAME "rmi"
#define DEVICE_CLASS_NAME "rmidev"
#define RMI_CHAR_DEV_TMPBUF_SZ (1024 * 8)
#define RMI_REG_ADDR_PAGE_SELECT 0xFF
#define REG_ADDR_LIMIT 0xFFFF
struct rmidev_data {
/* mutex for file operation*/
struct mutex file_mutex;
/* main char dev structure */
struct cdev main_dev;
/* pointer to the corresponding RMI4 device. We use this to do */
/* read, write, etc. */
struct platform_device *pdev;
struct clearpad_platform_data_t *pdata;
struct clearpad_bus_data_t *bdata;
/* reference count */
int ref_count;
struct class *device_class;
unsigned char tmpbuf[RMI_CHAR_DEV_TMPBUF_SZ];
};
/*store dynamically allocated major number of char device*/
static int rmidev_major_num;
static struct class *rmidev_device_class;
static int rmi_read_block(struct rmidev_data *data, u16 addr, u8 *buf, int len)
{
return data->bdata->read_block(data->bdata->dev, addr, buf, len);
}
static int rmi_write_block(struct rmidev_data *data, u16 addr, const u8 *buf,
int len)
{
return data->bdata->write_block(data->bdata->dev, addr, buf, len);
}
/* file operations for RMI char device */
/*
* rmidev_llseek: - use to setup 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(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 *data = filp->private_data;
if (IS_ERR(data)) {
pr_err("%s: pointer of char device is invalid", __func__);
newpos = -EBADF;
goto exit;
}
mutex_lock(&(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: /* can't happen */
newpos = -EINVAL;
goto clean_up;
}
if (newpos < 0 || newpos > REG_ADDR_LIMIT) {
dev_err(&data->pdev->dev, "newpos 0x%04x is invalid.\n",
(unsigned int)newpos);
newpos = -EINVAL;
goto clean_up;
}
filp->f_pos = newpos;
clean_up:
mutex_unlock(&(data->file_mutex));
exit:
return newpos;
}
/*
* rmidev_read: - use to read data from RMI stream
*
* @filp: file structure for read
* @buf: user-level buffer pointer
*
* @count: number of byte read
* @f_pos: offset (starting register address)
*
* @return number of bytes read into user buffer (buf) if succeeds
* negative number if error occurs.
*/
static ssize_t rmidev_read(struct file *filp, char __user *buf,
size_t count, loff_t *f_pos)
{
struct rmidev_data *data = filp->private_data;
ssize_t retval = 0;
if (*f_pos > REG_ADDR_LIMIT) {
retval = -EINVAL;
goto exit;
}
/* limit offset to REG_ADDR_LIMIT-1 */
if (count > (REG_ADDR_LIMIT - *f_pos))
count = REG_ADDR_LIMIT - *f_pos;
if (count > RMI_CHAR_DEV_TMPBUF_SZ)
count = RMI_CHAR_DEV_TMPBUF_SZ;
if (count == 0)
goto exit;
if (IS_ERR(data)) {
pr_err("%s: pointer of char device is invalid", __func__);
retval = -EBADF;
goto exit;
}
mutex_lock(&(data->file_mutex));
retval = rmi_read_block(data, *f_pos, data->tmpbuf, count);
if (retval < 0)
goto clean_up;
else
*f_pos += retval;
if (copy_to_user(buf, data->tmpbuf, count))
retval = -EFAULT;
clean_up:
mutex_unlock(&(data->file_mutex));
exit:
return retval;
}
/*
* rmidev_write: - use to write data into RMI stream
*
* @filep : file structure for write
* @buf: user-level buffer pointer contains data to be written
* @count: number of byte be be written
* @f_pos: offset (starting register address)
*
* @return number of bytes written from user buffer (buf) if succeeds
* negative number if error occurs.
*/
static ssize_t rmidev_write(struct file *filp, const char __user *buf,
size_t count, loff_t *f_pos)
{
struct rmidev_data *data = filp->private_data;
ssize_t retval = 0;
if (*f_pos > REG_ADDR_LIMIT) {
retval = -EINVAL;
goto exit;
}
/* limit offset to REG_ADDR_LIMIT-1 */
if (count > (REG_ADDR_LIMIT - *f_pos))
count = REG_ADDR_LIMIT - *f_pos;
if (count > RMI_CHAR_DEV_TMPBUF_SZ)
count = RMI_CHAR_DEV_TMPBUF_SZ;
if (count == 0)
goto exit;
if (IS_ERR(data)) {
pr_err("%s: pointer of char device is invalid", __func__);
retval = -EBADF;
goto exit;
}
if (copy_from_user(data->tmpbuf, buf, count)) {
retval = -EFAULT;
goto exit;
}
mutex_lock(&(data->file_mutex));
retval = rmi_write_block(data, *f_pos, data->tmpbuf, (int)count);
if (retval >= 0)
*f_pos += count;
mutex_unlock(&(data->file_mutex));
exit:
return retval;
}
/*
* rmidev_open: - get a new handle for from RMI stream
* @inp : inode struture
* @filp: file structure for read/write
*
* @return 0 if succeeds
*/
static int rmidev_open(struct inode *inp, struct file *filp)
{
struct rmidev_data *data = container_of(inp->i_cdev,
struct rmidev_data, main_dev);
int retval = 0;
filp->private_data = data;
if (!data->pdata) {
retval = -EACCES;
goto exit;
}
mutex_lock(&(data->file_mutex));
if (data->ref_count < 1)
data->ref_count++;
else
retval = -EACCES;
mutex_unlock(&(data->file_mutex));
exit:
return retval;
}
/*
* rmidev_release: - release an existing handle
* @inp: inode structure
* @filp: file structure for read/write
*
* @return 0 if succeeds
*/
static int rmidev_release(struct inode *inp, struct file *filp)
{
struct rmidev_data *data = container_of(inp->i_cdev,
struct rmidev_data, main_dev);
int retval = 0;
if (!data->pdev) {
retval = -EACCES;
goto exit;
}
mutex_lock(&(data->file_mutex));
data->ref_count--;
if (data->ref_count < 0)
data->ref_count = 0;
mutex_unlock(&(data->file_mutex));
exit:
return retval;
}
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,
};
/*
* rmi_char_dev_clean_up - release memory or unregister driver
* @rmi_char_dev: rmi_char_dev structure
*
*/
static void rmidev_device_cleanup(struct rmidev_data *data)
{
dev_t devno;
/* Get rid of our char dev entries */
if (data) {
devno = data->main_dev.dev;
if (data->device_class)
device_destroy(data->device_class, devno);
cdev_del(&data->main_dev);
/* cleanup_module is never called if registering failed */
unregister_chrdev_region(devno, 1);
pr_debug("%s: rmidev device is removed\n", __func__);
}
}
/*
* rmi_char_devnode - return device permission
*
* @dev: char device structure
* @mode: file permission
*
*/
static char *rmi_char_devnode(struct device *dev, umode_t *mode)
{
char *pret = NULL;
if (mode) {
/**mode = 0600*/
*mode = S_IRUSR|S_IWUSR;
pret = kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev));
}
return pret;
}
static int rmidev_init_device(struct rmidev_data *data)
{
dev_t dev_no;
int retval;
struct device *device_ptr;
if (rmidev_major_num) {
dev_no = MKDEV(rmidev_major_num, 0);
retval = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME);
} else {
retval = alloc_chrdev_region(&dev_no, 0, 1, CHAR_DEVICE_NAME);
/* let kernel allocate a major for us */
rmidev_major_num = MAJOR(dev_no);
dev_info(&data->pdev->dev, "Major number of rmidev: %d\n",
rmidev_major_num);
}
if (retval < 0) {
dev_err(&data->pdev->dev,
"Failed to register or allocate char dev, code %d.\n",
retval);
goto exit;
} else {
dev_info(&data->pdev->dev, "Allocated rmidev %d %d.\n",
MAJOR(dev_no), MINOR(dev_no));
}
mutex_init(&data->file_mutex);
cdev_init(&data->main_dev, &rmidev_fops);
retval = cdev_add(&data->main_dev, dev_no, 1);
if (retval) {
dev_err(&data->pdev->dev, "Error %d adding rmi_char_dev.\n",
retval);
rmidev_device_cleanup(data);
goto exit;
}
dev_set_name(&data->pdev->dev, "rmidev%d", MINOR(dev_no));
data->device_class = rmidev_device_class;
device_ptr = device_create(
data->device_class,
NULL, dev_no, NULL,
CHAR_DEVICE_NAME"%d",
MINOR(dev_no));
if (IS_ERR(device_ptr)) {
dev_err(&data->pdev->dev,
"Failed to create rmi device.\n");
rmidev_device_cleanup(data);
retval = -ENODEV;
}
exit:
return retval;
}
static int rmi_dev_probe(struct platform_device *pdev)
{
struct clearpad_data_t *cdata = pdev->dev.platform_data;
struct rmidev_data *data;
int retval = 0;
data = kzalloc(sizeof(struct rmidev_data), GFP_KERNEL);
if (!data) {
retval = -ENOMEM;
goto exit;
}
dev_set_drvdata(&pdev->dev, data);
data->pdev = pdev;
data->pdata = cdata->pdata;
if (!data->pdata) {
dev_err(&data->pdev->dev, "no platform data\n");
retval = -EINVAL;
goto err_free;
}
data->bdata = cdata->bdata;
if (!data->bdata) {
dev_err(&data->pdev->dev, "no bus data\n");
retval = -EINVAL;
goto err_free;
}
retval = rmidev_init_device(data);
if (!retval)
goto exit;
err_free:
dev_set_drvdata(&pdev->dev, NULL);
kfree(data);
exit:
return retval;
}
static int rmi_dev_remove(struct platform_device *pdev)
{
struct rmidev_data *data = dev_get_drvdata(&pdev->dev);
dev_set_drvdata(&pdev->dev, NULL);
kfree(data);
return 0;
}
static struct platform_driver rmidev_driver = {
.driver = {
.name = CLEARPAD_RMI_DEV_NAME,
.owner = THIS_MODULE,
},
.probe = rmi_dev_probe,
.remove = rmi_dev_remove,
};
static int __init rmidev_init(void)
{
int retval = 0;
pr_debug("%s: rmi_dev initialization.\n", __func__);
/* create device node */
rmidev_device_class = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
if (IS_ERR(rmidev_device_class)) {
pr_err("%s: ERROR - Failed to create /dev/%s.\n", __func__,
CHAR_DEVICE_NAME);
retval = -ENODEV;
goto exit;
}
/* setup permission */
rmidev_device_class->devnode = rmi_char_devnode;
retval = platform_driver_register(&rmidev_driver);
exit:
return retval;
}
static void __exit rmidev_exit(void)
{
pr_debug("%s: exiting.\n", __func__);
platform_driver_unregister(&rmidev_driver);
class_unregister(rmidev_device_class);
class_destroy(rmidev_device_class);
}
module_init(rmidev_init);
module_exit(rmidev_exit);
MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
MODULE_DESCRIPTION("RMI4 Char Device");
MODULE_LICENSE("GPL");

119
include/linux/clearpad.h Normal file
View file

@ -0,0 +1,119 @@
/* msm/clearpad/include/clearpad.h
*
* Copyright (C) 2017 Sony Mobile Communications Inc.
*
* Author: Courtney Cavin <courtney.cavin@sonyericsson.com>
* Yusuke Yoshimura <Yusuke.Yoshimura@sonymobile.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*/
#ifndef __LINUX_CLEARPAD_H
#define __LINUX_CLEARPAD_H
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#define CLEARPAD_NAME "clearpad"
#define CLEARPADI2C_NAME "clearpad-i2c"
#define CLEARPAD_RMI_DEV_NAME "clearpad-rmi-dev"
#define SYN_PCA_BLOCK_SIZE 16
#define SYN_PCA_BLOCK_NUMBER_MAX 31
//#define CLEARPAD_WAKEUP_GESTURE
enum clearpad_funcarea_kind_e {
SYN_FUNCAREA_INSENSIBLE,
SYN_FUNCAREA_POINTER,
SYN_FUNCAREA_BUTTON,
SYN_FUNCAREA_END,
};
enum clearpad_flip_config_e {
SYN_FLIP_NONE,
SYN_FLIP_X,
SYN_FLIP_Y,
SYN_FLIP_XY,
};
enum clearpad_infomation_attribute_kind_e {
PCA_DATA = 0x00,
PCA_IC = 0x01,
PCA_CHIP = 0x03,
PCA_MODULE = 0x05,
};
enum clearpad_infomation_kind_e {
PCA_NO_USE = 0x00,
PCA_FW_INFO = 0x02,
};
struct clearpad_area_t {
int x1;
int y1;
int x2;
int y2;
};
struct clearpad_funcarea_t {
struct clearpad_area_t original; /* actual area */
struct clearpad_area_t extension; /* extended area to track events */
enum clearpad_funcarea_kind_e func;
void *data;
};
struct clearpad_pointer_data_t {
int offset_x;
int offset_y;
};
struct clearpad_button_data_t {
int code;
bool down;
bool down_report;
};
struct clearpad_platform_data_t {
int irq_gpio;
u32 irq_gpio_flags;
char *symlink_name;
bool watchdog_enable;
int watchdog_poll_t_ms;
};
struct clearpad_bus_data_t {
__u16 bustype;
struct device *dev;
struct device_node *of_node;
int (*set_page)(struct device *dev, u8 page);
int (*read)(struct device *dev, u16 addr, u8 *buf, u8 len);
int (*write)(struct device *dev, u16 addr, const u8 *buf, u8 len);
int (*read_block)(struct device *dev, u16 addr, u8 *buf, int len);
int (*write_block)(struct device *dev, u16 addr, const u8 *buf,
int len);
};
struct clearpad_data_t {
struct clearpad_platform_data_t *pdata;
struct clearpad_bus_data_t *bdata;
int probe_retry;
struct platform_device *rmi_dev;
};
struct clearpad_ioctl_pca_info {
u16 block_pos;
u8 data[SYN_PCA_BLOCK_SIZE];
};
#define SYN_PCA_IOCTL_MAGIC 0xCC
#define SYN_PCA_IOCTL_GET \
_IOWR(SYN_PCA_IOCTL_MAGIC, 1, struct clearpad_ioctl_pca_info *)
#define SYN_PCA_IOCTL_SET \
_IOW(SYN_PCA_IOCTL_MAGIC, 2, struct clearpad_ioctl_pca_info *)
#endif

170
include/linux/evdt_helper.h Normal file
View file

@ -0,0 +1,170 @@
/* msm/clearpad/include/evdt_helper.h
*
* Copyright (C) 2017 Sony Mobile Communications Inc.
*
* Author: Kenji Suzuki <Kenji.X.Suzuki@sonymobile.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*/
#ifndef __LINUX_INPUT_EVDT_HELPER_H__
#define __LINUX_INPUT_EVDT_HELPER_H__
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/of.h>
enum {
EVDT_MSLEEP = 0,
EVDT_KEY,
EVDT_LOG,
EVDT_END = 99,
};
static inline int evdt_get_msleep(struct device_node *node, u32 *ms)
{
return of_property_read_u32(node, "ms", ms);
}
static inline int evdt_get_key(struct device_node *node, u32 *code, u32 *down)
{
int rc;
rc = of_property_read_u32(node, "code", code);
if (!rc)
rc = of_property_read_u32(node, "down", down);
return rc;
}
static inline int evdt_get_log(struct device_node *node, const char **message)
{
return of_property_read_string(node, "message", message);
}
static inline struct device_node *evdt_initialize(struct device *dev,
struct input_dev *input, const char *node)
{
struct device_node *wg_node = NULL;
struct device_node *block = NULL;
struct device_node *record = NULL;
int index = 0;
u32 type;
u32 ms;
u32 code;
u32 down;
const char *message;
int rc;
if (!dev->of_node || !input)
return NULL;
wg_node = of_find_node_by_name(dev->of_node, node);
if (!wg_node) {
pr_err("%s: No %s node found\n", __func__, node);
return NULL;
}
for_each_child_of_node(wg_node, block) {
index = 0;
for_each_child_of_node(block, record) {
rc = of_property_read_u32(record, "type", &type);
if (rc)
goto error;
switch (type) {
case EVDT_MSLEEP:
rc = evdt_get_msleep(record, &ms);
if (rc)
goto error;
pr_info("%s: MSLEEP [%u]\n", __func__, ms);
break;
case EVDT_KEY:
rc = evdt_get_key(record, &code, &down);
if (rc)
goto error;
input_set_capability(input, EV_KEY, code);
pr_info("%s: KEY [%u, %s]\n", __func__, code,
down ? "DOWN" : "UP");
break;
case EVDT_LOG:
rc = evdt_get_log(record, &message);
if (rc)
goto error;
pr_info("%s: LOG [%s]\n", __func__, message);
break;
case EVDT_END:
default:
break;
}
index++;
}
}
return wg_node;
error:
pr_err("%s: invalid value at index %d for %s rc=%d\n",
__func__, index, block->name, rc);
return NULL;
}
static inline int evdt_execute(struct device_node *block,
struct input_dev *input, u16 gesture_code)
{
struct device_node *gesture = NULL;
struct device_node *record;
u32 type;
u32 ms;
u32 code;
u32 down;
const char *message;
int rc;
if (!block || !input)
return 0;
for_each_child_of_node(block, gesture) {
rc = of_property_read_u32(gesture, "gesture_code", &type);
if (rc) {
pr_err("%s: Error reading gesture_code for %s rc=%d\n",
__func__, gesture->name, rc);
return rc;
}
if (gesture_code != type)
continue;
for_each_child_of_node(gesture, record) {
of_property_read_u32(record, "type", &type);
switch (type) {
case EVDT_MSLEEP:
evdt_get_msleep(record, &ms);
pr_debug("%s: MSLEEP(%d)\n", __func__, ms);
msleep(ms);
break;
case EVDT_KEY:
evdt_get_key(record, &code, &down);
pr_debug("%s: KEY(%d) %s\n", __func__, code,
down ? "DOWN" : "UP");
input_report_key(input, code, down);
input_sync(input);
break;
case EVDT_LOG:
evdt_get_log(record, &message);
pr_info("%s: %s\n", __func__, message);
break;
case EVDT_END:
default:
goto loop_end;
}
}
}
pr_warn("%s: Gesture code 0x%X not defined\n", __func__, gesture_code);
loop_end:
return 0;
}
#endif /* __LINUX_INPUT_EVDT_HELPER_H__ */

View file

@ -0,0 +1,170 @@
/* msm/clearpad/include/evdt_helper.h
*
* Copyright (C) 2017 Sony Mobile Communications Inc.
*
* Author: Kenji Suzuki <Kenji.X.Suzuki@sonymobile.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*/
#ifndef __LINUX_INPUT_EVDT_HELPER_H__
#define __LINUX_INPUT_EVDT_HELPER_H__
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/of.h>
enum {
EVDT_MSLEEP = 0,
EVDT_KEY,
EVDT_LOG,
EVDT_END = 99,
};
static inline int evdt_get_msleep(struct device_node *node, u32 *ms)
{
return of_property_read_u32(node, "ms", ms);
}
static inline int evdt_get_key(struct device_node *node, u32 *code, u32 *down)
{
int rc;
rc = of_property_read_u32(node, "code", code);
if (!rc)
rc = of_property_read_u32(node, "down", down);
return rc;
}
static inline int evdt_get_log(struct device_node *node, const char **message)
{
return of_property_read_string(node, "message", message);
}
static inline struct device_node *evdt_initialize(struct device *dev,
struct input_dev *input, const char *node)
{
struct device_node *wg_node = NULL;
struct device_node *block = NULL;
struct device_node *record = NULL;
int index = 0;
u32 type;
u32 ms;
u32 code;
u32 down;
const char *message;
int rc;
if (!dev->of_node || !input)
return NULL;
wg_node = of_find_node_by_name(dev->of_node, node);
if (!wg_node) {
pr_err("%s: No %s node found\n", __func__, node);
return NULL;
}
for_each_child_of_node(wg_node, block) {
index = 0;
for_each_child_of_node(block, record) {
rc = of_property_read_u32(record, "type", &type);
if (rc)
goto error;
switch (type) {
case EVDT_MSLEEP:
rc = evdt_get_msleep(record, &ms);
if (rc)
goto error;
pr_info("%s: MSLEEP [%u]\n", __func__, ms);
break;
case EVDT_KEY:
rc = evdt_get_key(record, &code, &down);
if (rc)
goto error;
input_set_capability(input, EV_KEY, code);
pr_info("%s: KEY [%u, %s]\n", __func__, code,
down ? "DOWN" : "UP");
break;
case EVDT_LOG:
rc = evdt_get_log(record, &message);
if (rc)
goto error;
pr_info("%s: LOG [%s]\n", __func__, message);
break;
case EVDT_END:
default:
break;
}
index++;
}
}
return wg_node;
error:
pr_err("%s: invalid value at index %d for %s rc=%d\n",
__func__, index, block->name, rc);
return NULL;
}
static inline int evdt_execute(struct device_node *block,
struct input_dev *input, u16 gesture_code)
{
struct device_node *gesture = NULL;
struct device_node *record;
u32 type;
u32 ms;
u32 code;
u32 down;
const char *message;
int rc;
if (!block || !input)
return 0;
for_each_child_of_node(block, gesture) {
rc = of_property_read_u32(gesture, "gesture_code", &type);
if (rc) {
pr_err("%s: Error reading gesture_code for %s rc=%d\n",
__func__, gesture->name, rc);
return rc;
}
if (gesture_code != type)
continue;
for_each_child_of_node(gesture, record) {
of_property_read_u32(record, "type", &type);
switch (type) {
case EVDT_MSLEEP:
evdt_get_msleep(record, &ms);
pr_debug("%s: MSLEEP(%d)\n", __func__, ms);
msleep(ms);
break;
case EVDT_KEY:
evdt_get_key(record, &code, &down);
pr_debug("%s: KEY(%d) %s\n", __func__, code,
down ? "DOWN" : "UP");
input_report_key(input, code, down);
input_sync(input);
break;
case EVDT_LOG:
evdt_get_log(record, &message);
pr_info("%s: %s\n", __func__, message);
break;
case EVDT_END:
default:
goto loop_end;
}
}
}
pr_warn("%s: Gesture code 0x%X not defined\n", __func__, gesture_code);
loop_end:
return 0;
}
#endif /* __LINUX_INPUT_EVDT_HELPER_H__ */