From cac584df2da86b1d2688284aa4f93787e5378870 Mon Sep 17 00:00:00 2001 From: Ali Ekici Date: Wed, 11 Jan 2012 14:10:13 -0800 Subject: input: touchscreen: DirectTouch Synaptics driver This is Synaptics' open source driver except one line change to accomodate an SPI kernel driver feature which does not accomodate optional byte-delay. It will be fully original driver when we add byte-delay into Tegra SPI kernel drivers. Bug 912775 Reviewed-on: http://git-master/r/74642 Change-Id: I0f39928c48bc0e633de0d2c1f595bcef47268d52 Signed-off-by: Peter Zu Signed-off-by: Varun Wadekar Reviewed-on: http://git-master/r/77774 Reviewed-by: Automatic_Commit_Validation_User Rebase-Id: Re743c63c9de16c4ecad3f38158fec3a62bc90f9b --- drivers/input/touchscreen/Kconfig | 12 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/rmi4/Makefile | 18 + drivers/input/touchscreen/rmi4/rmi_bus.c | 315 ++++++ drivers/input/touchscreen/rmi4/rmi_dev.c | 428 ++++++++ drivers/input/touchscreen/rmi4/rmi_driver.c | 1354 ++++++++++++++++++++++++ drivers/input/touchscreen/rmi4/rmi_driver.h | 109 ++ drivers/input/touchscreen/rmi4/rmi_f01.c | 775 ++++++++++++++ drivers/input/touchscreen/rmi4/rmi_f09.c | 298 ++++++ drivers/input/touchscreen/rmi4/rmi_f11.c | 1513 +++++++++++++++++++++++++++ drivers/input/touchscreen/rmi4/rmi_f19.c | 1419 +++++++++++++++++++++++++ drivers/input/touchscreen/rmi4/rmi_f34.c | 821 +++++++++++++++ drivers/input/touchscreen/rmi4/rmi_f54.c | 1347 ++++++++++++++++++++++++ drivers/input/touchscreen/rmi4/rmi_i2c.c | 421 ++++++++ drivers/input/touchscreen/rmi4/rmi_spi.c | 869 +++++++++++++++ include/linux/rmi.h | 656 ++++++++++++ 16 files changed, 10356 insertions(+) create mode 100644 drivers/input/touchscreen/rmi4/Makefile create mode 100644 drivers/input/touchscreen/rmi4/rmi_bus.c create mode 100644 drivers/input/touchscreen/rmi4/rmi_dev.c create mode 100644 drivers/input/touchscreen/rmi4/rmi_driver.c create mode 100644 drivers/input/touchscreen/rmi4/rmi_driver.h create mode 100644 drivers/input/touchscreen/rmi4/rmi_f01.c create mode 100644 drivers/input/touchscreen/rmi4/rmi_f09.c create mode 100644 drivers/input/touchscreen/rmi4/rmi_f11.c create mode 100644 drivers/input/touchscreen/rmi4/rmi_f19.c create mode 100644 drivers/input/touchscreen/rmi4/rmi_f34.c create mode 100644 drivers/input/touchscreen/rmi4/rmi_f54.c create mode 100644 drivers/input/touchscreen/rmi4/rmi_i2c.c create mode 100644 drivers/input/touchscreen/rmi4/rmi_spi.c create mode 100644 include/linux/rmi.h diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index f5182cf4569c..fae55b5df30d 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -901,4 +901,16 @@ config TOUCHSCREEN_TPS6507X To compile this driver as a module, choose M here: the module will be called tps6507x_ts. +config TOUCHSCREEN_SYN_RMI4_SPI + tristate "RMI4 SPI Support" + depends on SPI_MASTER + help + Say Y here if you want to support RMI4 devices connect + to an SPI bus. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called rmi-spi. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 69f6a4d9a1f0..4d0ef353025d 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -73,3 +73,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o +obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi4/ diff --git a/drivers/input/touchscreen/rmi4/Makefile b/drivers/input/touchscreen/rmi4/Makefile new file mode 100644 index 000000000000..a7375ed07a39 --- /dev/null +++ b/drivers/input/touchscreen/rmi4/Makefile @@ -0,0 +1,18 @@ +# +# Makefile for Synaptics Touchscreen (RMI4/SPI) +# +GCOV_PROFILE := y + +ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG + +#Synaptics SPI Sensor (2002) +obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_bus.o +obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_i2c.o +obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_spi.o +obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_driver.o rmi_f01.o +obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_f09.o +obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_f11.o +obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_f19.o +obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_f34.o +obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_f54.o +obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_dev.o diff --git a/drivers/input/touchscreen/rmi4/rmi_bus.c b/drivers/input/touchscreen/rmi4/rmi_bus.c new file mode 100644 index 000000000000..6a269df4ff35 --- /dev/null +++ b/drivers/input/touchscreen/rmi4/rmi_bus.c @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2011 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include + +static struct rmi_function_list { + struct list_head list; + struct rmi_function_handler *fh; +} rmi_supported_functions; + +static int rmi_bus_match(struct device *dev, struct device_driver *driver) +{ + struct rmi_driver *rmi_driver; + struct rmi_device *rmi_dev; + struct rmi_device_platform_data *pdata; + + pr_info("in function ____%s____ \n", __func__); + rmi_driver = to_rmi_driver(driver); + rmi_dev = to_rmi_device(dev); + pdata = to_rmi_platform_data(rmi_dev); + + pr_info(" rmi_driver->driver.name = %s\n", rmi_driver->driver.name); + pr_info(" device:rmi_device = 0x%x \n", rmi_dev); + pr_info(" device:rmi_device:rmi_device_platform_data:driver_name = %s \n", pdata->driver_name); + pr_info(" rmi_device:driver = 0x%x \n", rmi_dev->driver); + + if (!strcmp(pdata->driver_name, rmi_driver->driver.name)) { + rmi_dev->driver = rmi_driver; + pr_info(" names match, so now rmi_device:driver = 0x%x \n",rmi_dev->driver); + return 1; + } + pr_info(" names DO NOT match, so return nothing \n"); + + return 0; +} + +#ifdef CONFIG_PM +static int rmi_bus_suspend(struct device *dev) +{ +#ifdef GENERIC_SUBSYS_PM_OPS + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm && pm->suspend) + return pm->suspend(dev); +#endif + + return 0; +} + +static int rmi_bus_resume(struct device *dev) +{ +#ifdef GENERIC_SUBSYS_PM_OPS + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + pr_info("in function ____%s____ \n", __func__); + + if (pm && pm->resume) + return pm->resume(dev); +#endif + + return 0; +} +#endif + +static int rmi_bus_probe(struct device *dev) +{ + struct rmi_driver *driver; + struct rmi_device *rmi_dev = to_rmi_device(dev); + + pr_info("in function ____%s____ \n", __func__); + driver = rmi_dev->driver; + if (driver && driver->probe) + return driver->probe(rmi_dev); + + return 0; +} + +static int rmi_bus_remove(struct device *dev) +{ + struct rmi_driver *driver; + struct rmi_device *rmi_dev = to_rmi_device(dev); + + pr_info("in function ____%s____ \n", __func__); + driver = rmi_dev->driver; + if (driver && driver->remove) + return driver->remove(rmi_dev); + + return 0; +} + +static void rmi_bus_shutdown(struct device *dev) +{ + struct rmi_driver *driver; + struct rmi_device *rmi_dev = to_rmi_device(dev); + + driver = rmi_dev->driver; + if (driver && driver->shutdown) + driver->shutdown(rmi_dev); +} + +static SIMPLE_DEV_PM_OPS(rmi_bus_pm_ops, + rmi_bus_suspend, rmi_bus_resume); + +struct bus_type rmi_bus_type = { + .name = "rmi", + .match = rmi_bus_match, + .probe = rmi_bus_probe, + .remove = rmi_bus_remove, + .shutdown = rmi_bus_shutdown, + .pm = &rmi_bus_pm_ops +}; + +int rmi_register_phys_device(struct rmi_phys_device *phys) +{ + static int phys_device_num; + struct rmi_device_platform_data *pdata = phys->dev->platform_data; + struct rmi_device *rmi_dev; + + pr_info("in function ____%s____ \n", __func__); + + if (!pdata) { + dev_err(phys->dev, "no platform data!\n"); + return -EINVAL; + } + + rmi_dev = kzalloc(sizeof(struct rmi_device), GFP_KERNEL); + if (!rmi_dev) + return -ENOMEM; + + rmi_dev->phys = phys; + rmi_dev->dev.bus = &rmi_bus_type; + dev_set_name(&rmi_dev->dev, "sensor%02d", phys_device_num++); + + phys->rmi_dev = rmi_dev; + pr_info(" registering physical device:\n"); + pr_info(" dev.init_name = \n", rmi_dev->dev.init_name); + pr_info(" dev.bus->name = \n", rmi_dev->dev.bus->name); + return device_register(&rmi_dev->dev); +} +EXPORT_SYMBOL(rmi_register_phys_device); + +void rmi_unregister_phys_device(struct rmi_phys_device *phys) +{ + struct rmi_device *rmi_dev = phys->rmi_dev; + pr_info("in function ____%s____ \n", __func__); + + device_unregister(&rmi_dev->dev); + kfree(rmi_dev); +} +EXPORT_SYMBOL(rmi_unregister_phys_device); + +int rmi_register_driver(struct rmi_driver *driver) +{ + pr_info("in function ____%s____ \n", __func__); + driver->driver.bus = &rmi_bus_type; + return driver_register(&driver->driver); +} +EXPORT_SYMBOL(rmi_register_driver); + +static int __rmi_driver_remove(struct device *dev, void *data) +{ + struct rmi_driver *driver = data; + struct rmi_device *rmi_dev = to_rmi_device(dev); + + if (rmi_dev->driver == driver) + rmi_dev->driver = NULL; + + return 0; +} + +void rmi_unregister_driver(struct rmi_driver *driver) +{ + bus_for_each_dev(&rmi_bus_type, NULL, driver, __rmi_driver_remove); + driver_unregister(&driver->driver); +} +EXPORT_SYMBOL(rmi_unregister_driver); + +static int __rmi_bus_fh_add(struct device *dev, void *data) +{ + struct rmi_driver *driver; + struct rmi_device *rmi_dev = to_rmi_device(dev); + pr_info("in function ____%s____ \n", __func__); + + driver = rmi_dev->driver; + if (driver && driver->fh_add) + driver->fh_add(rmi_dev, data); + + return 0; +} + +int rmi_register_function_driver(struct rmi_function_handler *fh) +{ + struct rmi_function_list *entry; + struct rmi_function_handler *fh_dup; + + fh_dup = rmi_get_function_handler(fh->func); + if (fh_dup) { + pr_err("%s: function f%.2x already registered!\n", __func__, + fh->func); + return -EINVAL; + } + + entry = kzalloc(sizeof(struct rmi_function_list), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->fh = fh; + list_add_tail(&entry->list, &rmi_supported_functions.list); + + /* notify devices of the new function handler */ + bus_for_each_dev(&rmi_bus_type, NULL, fh, __rmi_bus_fh_add); + + return 0; +} +EXPORT_SYMBOL(rmi_register_function_driver); + +static int __rmi_bus_fh_remove(struct device *dev, void *data) +{ + struct rmi_driver *driver; + struct rmi_device *rmi_dev = to_rmi_device(dev); + + pr_info("in function ____%s____ \n", __func__); + driver = rmi_dev->driver; + if (driver && driver->fh_remove) + driver->fh_remove(rmi_dev, data); + + return 0; +} + +void rmi_unregister_function_driver(struct rmi_function_handler *fh) +{ + struct rmi_function_list *entry, *n; + pr_info("in function ____%s____ \n", __func__); + + /* notify devices of the removal of the function handler */ + bus_for_each_dev(&rmi_bus_type, NULL, fh, __rmi_bus_fh_remove); + + list_for_each_entry_safe(entry, n, &rmi_supported_functions.list, list) + if (entry->fh->func == fh->func) { + list_del(&entry->list); + kfree(entry); + } +} +EXPORT_SYMBOL(rmi_unregister_function_driver); + +struct rmi_function_handler *rmi_get_function_handler(int id) +{ + struct rmi_function_list *entry; + pr_info("in function ____%s____ \n", __func__); + + list_for_each_entry(entry, &rmi_supported_functions.list, list) + if (entry->fh->func == id) + return entry->fh; + + return NULL; +} +EXPORT_SYMBOL(rmi_get_function_handler); + +static int __init rmi_bus_init(void) +{ + int error; + + pr_info("in function ____%s____ \n", __func__); + INIT_LIST_HEAD(&rmi_supported_functions.list); + + error = bus_register(&rmi_bus_type); + if (error < 0) { + pr_err("%s: error registering the RMI bus: %d\n", __func__, + error); + return error; + } + pr_info("%s: successfully registered RMI bus.\n", __func__); + + return 0; +} + +static void __exit rmi_bus_exit(void) +{ + struct rmi_function_list *entry, *n; + pr_info("in function ____%s____ \n", __func__); + + list_for_each_entry_safe(entry, n, &rmi_supported_functions.list, + list) { + list_del(&entry->list); + kfree(entry); + } + + bus_unregister(&rmi_bus_type); +} + +module_init(rmi_bus_init); +module_exit(rmi_bus_exit); + +MODULE_AUTHOR("Eric Andersson "); +MODULE_DESCRIPTION("RMI bus"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/rmi4/rmi_dev.c b/drivers/input/touchscreen/rmi4/rmi_dev.c new file mode 100644 index 000000000000..fa21ef9b20fa --- /dev/null +++ b/drivers/input/touchscreen/rmi4/rmi_dev.c @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2011 Synaptics Incorporated + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "rmi_driver.h" + +#define CHAR_DEVICE_NAME "rmi" + +#define REG_ADDR_LIMIT 0xFFFF + +/*store dynamically allocated major number of char device*/ +static int rmi_char_dev_major_num; + + +/* file operations for RMI char device */ + +/* + * rmi_char_dev_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 rmi_char_dev_llseek(struct file *filp, loff_t off, int whence) +{ + loff_t newpos; + struct rmi_char_dev *my_char_dev = filp->private_data; + + if (IS_ERR(my_char_dev)) { + pr_err("%s: pointer of char device is invalid", __func__); + return -EBADF; + } + + mutex_lock(&(my_char_dev->mutex_file_op)); + + 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(my_char_dev->phys->dev, "newpos 0x%04x is invalid.\n", + (unsigned int)newpos); + newpos = -EINVAL; + goto clean_up; + } + + filp->f_pos = newpos; + +clean_up: + mutex_unlock(&(my_char_dev->mutex_file_op)); + return newpos; +} + +/* + * rmi_char_dev_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 rmi_char_dev_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + struct rmi_char_dev *my_char_dev = filp->private_data; + ssize_t ret_value = 0; + unsigned char tmpbuf[count+1]; + struct rmi_phys_device *phys; + + /* limit offset to REG_ADDR_LIMIT-1 */ + if (count > (REG_ADDR_LIMIT - *f_pos)) + count = REG_ADDR_LIMIT - *f_pos; + + if (count == 0) + return 0; + + if (IS_ERR(my_char_dev)) { + pr_err("%s: pointer of char device is invalid", __func__); + ret_value = -EBADF; + return ret_value; + } + + mutex_lock(&(my_char_dev->mutex_file_op)); + + phys = my_char_dev->phys; + /* + * just let it go through , because we do not know the register is FIFO + * register or not + */ + + ret_value = phys->read_block(phys, *f_pos, tmpbuf, count); + + if (ret_value < 0) + goto clean_up; + else + *f_pos += ret_value; + + if (copy_to_user(buf, tmpbuf, count)) + ret_value = -EFAULT; + +clean_up: + + mutex_unlock(&(my_char_dev->mutex_file_op)); + + return ret_value; +} + +/* + * rmi_char_dev_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 rmi_char_dev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + struct rmi_char_dev *my_char_dev = filp->private_data; + ssize_t ret_value = 0; + unsigned char tmpbuf[count+1]; + struct rmi_phys_device *phys; + + /* limit offset to REG_ADDR_LIMIT-1 */ + if (count > (REG_ADDR_LIMIT - *f_pos)) + count = REG_ADDR_LIMIT - *f_pos; + + if (count == 0) + return 0; + + if (IS_ERR(my_char_dev)) { + pr_err("%s: pointer of char device is invalid", __func__); + ret_value = -EBADF; + return ret_value; + } + + if (copy_from_user(tmpbuf, buf, count)) { + ret_value = -EFAULT; + return ret_value; + } + + mutex_lock(&(my_char_dev->mutex_file_op)); + + phys = my_char_dev->phys; + /* + * just let it go through , because we do not know the register is FIFO + * register or not + */ + + ret_value = phys->write_block(phys, *f_pos, tmpbuf, count); + + if (ret_value >= 0) + *f_pos += count; + + mutex_unlock(&(my_char_dev->mutex_file_op)); + + return ret_value; +} + +/* + * rmi_char_dev_open: - get a new handle for from RMI stream + * @inp : inode struture + * @filp: file structure for read/write + * + * @return 0 if succeeds + */ +static int rmi_char_dev_open(struct inode *inp, struct file *filp) +{ + /* store the device pointer to file structure */ + struct rmi_char_dev *my_dev = container_of(inp->i_cdev, + struct rmi_char_dev, main_dev); + struct rmi_phys_device *phys = my_dev->phys; + int ret_value = 0; + + filp->private_data = my_dev; + + if (!phys) + return -EACCES; + + mutex_lock(&(my_dev->mutex_file_op)); + if (my_dev->ref_count < 1) + my_dev->ref_count++; + else + ret_value = -EACCES; + + mutex_unlock(&(my_dev->mutex_file_op)); + + return ret_value; +} + +/* + * rmi_char_dev_release: - release an existing handle + * @inp: inode structure + * @filp: file structure for read/write + * + * @return 0 if succeeds + */ +static int rmi_char_dev_release(struct inode *inp, struct file *filp) +{ + struct rmi_char_dev *my_dev = container_of(inp->i_cdev, + struct rmi_char_dev, main_dev); + struct rmi_phys_device *phys = my_dev->phys; + + if (!phys) + return -EACCES; + + mutex_lock(&(my_dev->mutex_file_op)); + + my_dev->ref_count--; + if (my_dev->ref_count < 0) + my_dev->ref_count = 0; + + mutex_unlock(&(my_dev->mutex_file_op)); + + return 0; +} + +static const struct file_operations rmi_char_dev_fops = { + .owner = THIS_MODULE, + .llseek = rmi_char_dev_llseek, + .read = rmi_char_dev_read, + .write = rmi_char_dev_write, + .open = rmi_char_dev_open, + .release = rmi_char_dev_release, +}; + +/* + * rmi_char_dev_clean_up - release memory or unregister driver + * @rmi_char_dev: rmi_char_dev structure + * + */ +static void rmi_char_dev_clean_up(struct rmi_char_dev *char_dev, + struct class *char_device_class) +{ + dev_t devno; + + /* Get rid of our char dev entries */ + if (char_dev) { + devno = char_dev->main_dev.dev; + + cdev_del(&char_dev->main_dev); + kfree(char_dev); + + if (char_device_class) { + device_destroy(char_device_class, devno); + class_unregister(char_device_class); + class_destroy(char_device_class); + } + + /* cleanup_module is never called if registering failed */ + unregister_chrdev_region(devno, 1); + pr_debug("%s: rmi_char_dev 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, mode_t *mode) +{ + if (!mode) + return NULL; + /* rmi** */ + /**mode = 0666*/ + *mode = (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); + + return kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev)); +} + +/* + * rmi_char_dev_register - register char device (called from up-level) + * + * @phy: a pointer to an rmi_phys_devices structure + * + * @return: zero if suceeds + */ +int rmi_char_dev_register(struct rmi_phys_device *phys) +{ + struct rmi_char_dev *char_dev; + dev_t dev_no; + int err; + int result; + struct device *device_ptr; + + if (rmi_char_dev_major_num) { + dev_no = MKDEV(rmi_char_dev_major_num, 0); + result = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME); + } else { + result = alloc_chrdev_region(&dev_no, 0, 1, CHAR_DEVICE_NAME); + /* let kernel allocate a major for us */ + rmi_char_dev_major_num = MAJOR(dev_no); + dev_info(phys->dev, "Major number of rmi_char_dev: %d\n", + rmi_char_dev_major_num); + } + if (result < 0) + return result; + + char_dev = kzalloc(sizeof(struct rmi_char_dev), GFP_KERNEL); + if (!char_dev) { + dev_err(phys->dev, "Failed to allocate rmi_char_dev.\n"); + /* unregister the char device region */ + __unregister_chrdev(rmi_char_dev_major_num, MINOR(dev_no), 1, + CHAR_DEVICE_NAME); + return -ENOMEM; + } + + mutex_init(&char_dev->mutex_file_op); + + phys->char_dev = char_dev; + char_dev->phys = phys; + + cdev_init(&char_dev->main_dev, &rmi_char_dev_fops); + + err = cdev_add(&char_dev->main_dev, dev_no, 1); + if (err) { + dev_err(phys->dev, "Error %d adding rmi_char_dev.\n", err); + rmi_char_dev_clean_up(phys->char_dev, + phys->rmi_char_device_class); + return err; + } + + /* create device node */ + phys->rmi_char_device_class = + class_create(THIS_MODULE, CHAR_DEVICE_NAME); + + if (IS_ERR(phys->rmi_char_device_class)) { + dev_err(phys->dev, "Failed to create /dev/%s.\n", + CHAR_DEVICE_NAME); + rmi_char_dev_clean_up(phys->char_dev, + phys->rmi_char_device_class); + return -ENODEV; + } + /* setup permission */ + phys->rmi_char_device_class->devnode = rmi_char_devnode; + + /* class creation */ + device_ptr = device_create( + phys->rmi_char_device_class, + NULL, dev_no, NULL, + CHAR_DEVICE_NAME"%d", + MINOR(dev_no)); + + if (IS_ERR(device_ptr)) { + dev_err(phys->dev, "Failed to create rmi device.\n"); + rmi_char_dev_clean_up(phys->char_dev, + phys->rmi_char_device_class); + return -ENODEV; + } + + return 0; +} +EXPORT_SYMBOL(rmi_char_dev_register); + +/* rmi_char_dev_unregister - unregister char device (called from up-level) + * + * @phys: pointer to an rmi_phys_device structure + */ + +void rmi_char_dev_unregister(struct rmi_phys_device *phys) +{ + /* clean up */ + if (phys) + rmi_char_dev_clean_up(phys->char_dev, + phys->rmi_char_device_class); +} +EXPORT_SYMBOL(rmi_char_dev_unregister); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("RMI4 Char Device"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/rmi4/rmi_driver.c b/drivers/input/touchscreen/rmi4/rmi_driver.c new file mode 100644 index 000000000000..33638158df56 --- /dev/null +++ b/drivers/input/touchscreen/rmi4/rmi_driver.c @@ -0,0 +1,1354 @@ +/* + * Copyright (c) 2011 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This driver adds support for generic RMI4 devices from Synpatics. It + * implements the mandatory f01 RMI register and depends on the presence of + * other required RMI functions. + * + * The RMI4 specification can be found here (URL split after files/ for + * style reasons): + * http://www.synaptics.com/sites/default/files/ + * 511-000136-01-Rev-E-RMI4%20Intrfacing%20Guide.pdf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rmi_driver.h" + +#define DELAY_DEBUG 0 +#define REGISTER_DEBUG 0 + +#define PDT_END_SCAN_LOCATION 0x0005 +#define PDT_PROPERTIES_LOCATION 0x00EF +#define BSR_LOCATION 0x00FE +#define HAS_BSR_MASK 0x20 +#define HAS_NONSTANDARD_PDT_MASK 0x40 +#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff) +#define RMI4_MAX_PAGE 0xff +#define RMI4_PAGE_SIZE 0x100 + +#define RMI_DEVICE_RESET_CMD 0x01 +#define INITIAL_RESET_WAIT_MS 20 + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void rmi_driver_early_suspend(struct early_suspend *h); +static void rmi_driver_late_resume(struct early_suspend *h); +#endif + + +/* sysfs files for attributes for driver values. */ +static ssize_t rmi_driver_hasbsr_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_driver_bsr_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_driver_bsr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static ssize_t rmi_driver_enabled_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_driver_enabled_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static ssize_t rmi_driver_phys_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_driver_version_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +#if REGISTER_DEBUG +static ssize_t rmi_driver_reg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +#endif + +#if DELAY_DEBUG +static ssize_t rmi_delay_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_delay_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +#endif + +static struct device_attribute attrs[] = { + __ATTR(hasbsr, RMI_RO_ATTR, + rmi_driver_hasbsr_show, rmi_store_error), + __ATTR(bsr, RMI_RW_ATTR, + rmi_driver_bsr_show, rmi_driver_bsr_store), + __ATTR(enabled, RMI_RW_ATTR, + rmi_driver_enabled_show, rmi_driver_enabled_store), + __ATTR(phys, RMI_RO_ATTR, + rmi_driver_phys_show, rmi_store_error), +#if REGISTER_DEBUG + __ATTR(reg, RMI_WO_ATTR, + rmi_show_error, rmi_driver_reg_store), +#endif +#if DELAY_DEBUG + __ATTR(delay, RMI_RW_ATTR, + rmi_delay_show, rmi_delay_store), +#endif + __ATTR(version, RMI_RO_ATTR, + rmi_driver_version_show, rmi_store_error), +}; + + +/* +** ONLY needed for POLLING mode of the driver +*/ +struct rmi_device *polled_synaptics_rmi_device = NULL; +EXPORT_SYMBOL(polled_synaptics_rmi_device); + +/* Useful helper functions for u8* */ + +void u8_set_bit(u8 *target, int pos) +{ + target[pos/8] |= 1<pdt_props & HAS_BSR_MASK) != 0; +} + +/* Utility routine to set bits in a register. */ +int rmi_set_bits(struct rmi_device *rmi_dev, unsigned short address, + unsigned char bits) +{ + unsigned char reg_contents; + int retval; + + retval = rmi_read_block(rmi_dev, address, ®_contents, 1); + if (retval) + return retval; + reg_contents = reg_contents | bits; + retval = rmi_write_block(rmi_dev, address, ®_contents, 1); + if (retval == 1) + return 0; + else if (retval == 0) + return -EIO; + return retval; +} +EXPORT_SYMBOL(rmi_set_bits); + +/* Utility routine to clear bits in a register. */ +int rmi_clear_bits(struct rmi_device *rmi_dev, unsigned short address, + unsigned char bits) +{ + unsigned char reg_contents; + int retval; + + retval = rmi_read_block(rmi_dev, address, ®_contents, 1); + if (retval) + return retval; + reg_contents = reg_contents & ~bits; + retval = rmi_write_block(rmi_dev, address, ®_contents, 1); + if (retval == 1) + return 0; + else if (retval == 0) + return -EIO; + return retval; +} +EXPORT_SYMBOL(rmi_clear_bits); + +static void rmi_free_function_list(struct rmi_device *rmi_dev) +{ + struct rmi_function_container *entry, *n; + struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev); + + list_for_each_entry_safe(entry, n, &data->rmi_functions.list, list) { + kfree(entry->irq_mask); + list_del(&entry->list); + } +} + +static int init_one_function(struct rmi_device *rmi_dev, + struct rmi_function_container *fc) +{ + int retval; + + dev_info(&rmi_dev->dev, "Initializing F%02X.\n", + fc->fd.function_number); + dev_dbg(&rmi_dev->dev, "Initializing F%02X.\n", + fc->fd.function_number); + + if (!fc->fh) { + struct rmi_function_handler *fh = + rmi_get_function_handler(fc->fd.function_number); + if (!fh) { + dev_dbg(&rmi_dev->dev, "No handler for F%02X.\n", + fc->fd.function_number); + return 0; + } + fc->fh = fh; + } + + if (!fc->fh->init) + return 0; + + dev_set_name(&(fc->dev), "fn%02x", fc->fd.function_number); + + fc->dev.parent = &rmi_dev->dev; + + retval = device_register(&fc->dev); + if (retval) { + dev_err(&rmi_dev->dev, "Failed device_register for F%02X.\n", + fc->fd.function_number); + return retval; + } + + retval = fc->fh->init(fc); + if (retval < 0) { + dev_err(&rmi_dev->dev, "Failed to initialize function F%02x\n", + fc->fd.function_number); + goto error_exit; + } + + return 0; + +error_exit: + device_unregister(&fc->dev); + return retval; +} + +static void rmi_driver_fh_add(struct rmi_device *rmi_dev, + struct rmi_function_handler *fh) +{ + struct rmi_function_container *entry; + struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev); + + if (fh->func == 0x01) + data->f01_container->fh = fh; + else { + list_for_each_entry(entry, &data->rmi_functions.list, list) + if (entry->fd.function_number == fh->func) { + entry->fh = fh; + init_one_function(rmi_dev, entry); + } + } + +} + +static void rmi_driver_fh_remove(struct rmi_device *rmi_dev, + struct rmi_function_handler *fh) +{ + struct rmi_function_container *entry; + struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev); + + list_for_each_entry(entry, &data->rmi_functions.list, list) + if (entry->fd.function_number == fh->func) { + if (fh->remove) + fh->remove(entry); + + entry->fh = NULL; + } +} + +static void construct_mask(u8 *mask, int num, int pos) +{ + int i; + + for (i = 0; i < num; i++) + u8_set_bit(mask, pos+i); +} + +static int process_interrupt_requests(struct rmi_device *rmi_dev) +{ + struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev); + struct device *dev = &rmi_dev->dev; + struct rmi_function_container *entry; + u8 irq_status[data->num_of_irq_regs]; + u8 irq_bits[data->num_of_irq_regs]; + int error; + + error = rmi_read_block(rmi_dev, + data->f01_container->fd.data_base_addr + 1, + irq_status, data->num_of_irq_regs); + if (error < 0) { + dev_err(dev, "%s: failed to read irqs.", __func__); + return error; + } + /* Device control (F01) is handled before anything else. */ + u8_and(irq_bits, irq_status, data->f01_container->irq_mask, + data->num_of_irq_regs); + if (u8_is_any_set(irq_bits, data->num_of_irq_regs)) + data->f01_container->fh->attention( + data->f01_container, irq_bits); + + //dev_info(dev, " irq_status = 0x%2x data->current_irq_mask = 0x%2x data->num_of_irq_regs = %d\n", + // irq_status[0], data->current_irq_mask[0], data->num_of_irq_regs ); + + + u8_and(irq_status, irq_status, data->current_irq_mask, + data->num_of_irq_regs); + + /* At this point, irq_status has all bits that are set in the + * interrupt status register and are enabled. + */ + + list_for_each_entry(entry, &data->rmi_functions.list, list){ + if (entry->irq_mask && entry->fh && entry->fh->attention) { + + u8_and(irq_bits, irq_status, entry->irq_mask, + data->num_of_irq_regs); + if (u8_is_any_set(irq_bits, data->num_of_irq_regs)) { + error = entry->fh->attention(entry, irq_bits); + if (error < 0) + dev_err(dev, "%s: f%.2x" + " attention handler failed:" + " %d\n", __func__, + entry->fh->func, error); + } + } + } + return 0; +} + + +static int rmi_driver_irq_handler(struct rmi_device *rmi_dev, int irq) +{ + struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev); + + /* Can get called before the driver is fully ready to deal with + * interrupts. + */ + if (!data || !data->f01_container || !data->f01_container->fh) { + dev_warn(&rmi_dev->dev, + "Not ready to handle interrupts yet!\n"); + return 0; + } + + return process_interrupt_requests(rmi_dev); +} + +/* + * Construct a function's IRQ mask. This should + * be called once and stored. + */ +static u8 *rmi_driver_irq_get_mask(struct rmi_device *rmi_dev, + struct rmi_function_container *fc) { + struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev); + + /* TODO: Where should this be freed? */ + u8 *irq_mask = kzalloc(sizeof(u8) * data->num_of_irq_regs, GFP_KERNEL); + if (irq_mask) + construct_mask(irq_mask, fc->num_of_irqs, fc->irq_pos); + + return irq_mask; +} + +/* + * This pair of functions allows functions like function 54 to request to have + * other interupts disabled until the restore function is called. Only one store + * happens at a time. + */ +static int rmi_driver_irq_save(struct rmi_device *rmi_dev, u8 * new_ints) +{ + int retval = 0; + struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev); + struct device *dev = &rmi_dev->dev; + + mutex_lock(&data->irq_mutex); + if (!data->irq_stored) { + /* Save current enabled interupts */ + retval = rmi_read_block(rmi_dev, + data->f01_container->fd.control_base_addr+1, + data->irq_mask_store, data->num_of_irq_regs); + if (retval < 0) { + dev_err(dev, "%s: Failed to read enabled interrupts!", + __func__); + goto error_unlock; + } + /* + * Disable every interupt except for function 54 + * TODO:Will also want to not disable function 1-like functions. + * No need to take care of this now, since there's no good way + * to identify them. + */ + retval = rmi_write_block(rmi_dev, + data->f01_container->fd.control_base_addr+1, + new_ints, data->num_of_irq_regs); + if (retval < 0) { + dev_err(dev, "%s: Failed to change enabled interrupts!", + __func__); + goto error_unlock; + } + memcpy(data->current_irq_mask, new_ints, + data->num_of_irq_regs * sizeof(u8)); + data->irq_stored = true; + } else { + retval = -ENOSPC; /* No space to store IRQs.*/ + dev_err(dev, "%s: Attempted to save values when" + " already stored!", __func__); + } + +error_unlock: + mutex_unlock(&data->irq_mutex); + return retval; +} + +static int rmi_driver_irq_restore(struct rmi_device *rmi_dev) +{ + int retval = 0; + struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev); + struct device *dev = &rmi_dev->dev; + mutex_lock(&data->irq_mutex); + + if (data->irq_stored) { + retval = rmi_write_block(rmi_dev, + data->f01_container->fd.control_base_addr+1, + data->irq_mask_store, data->num_of_irq_regs); + if (retval < 0) { + dev_err(dev, "%s: Failed to write enabled interupts!", + __func__); + goto error_unlock; + } + memcpy(data->current_irq_mask, data->irq_mask_store, + data->num_of_irq_regs * sizeof(u8)); + data->irq_stored = false; + } else { + retval = -EINVAL; + dev_err(dev, "%s: Attempted to restore values when not stored!", + __func__); + } + +error_unlock: + mutex_unlock(&data->irq_mutex); + return retval; +} + +static int rmi_driver_fn_01_specific(struct rmi_device *rmi_dev, + struct pdt_entry *pdt_ptr, + int *current_irq_count, + u16 page_start) +{ + struct rmi_driver_data *data = NULL; + struct rmi_function_container *fc = NULL; + int retval = 0; + struct device *dev = &rmi_dev->dev; + struct rmi_function_handler *fh = + rmi_get_function_handler(0x01); + + data = rmi_get_driverdata(rmi_dev); + + dev_info(dev, "%s: Found F01, initializing.\n", __func__); + if (!fh) + dev_dbg(dev, "%s: No function handler for F01?!", __func__); + + fc = kzalloc(sizeof(struct rmi_function_container), GFP_KERNEL); + if (!fc) { + retval = -ENOMEM; + return retval; + } + + copy_pdt_entry_to_fd(pdt_ptr, &fc->fd, page_start); + fc->num_of_irqs = pdt_ptr->interrupt_source_count; + fc->irq_pos = *current_irq_count; + + *current_irq_count += fc->num_of_irqs; + + fc->rmi_dev = rmi_dev; + fc->dev.parent = &fc->rmi_dev->dev; + fc->fh = fh; + + dev_set_name(&(fc->dev), "fn%02x", fc->fd.function_number); + + retval = device_register(&fc->dev); + if (retval) { + dev_err(dev, "%s: Failed device_register for F01.\n", __func__); + goto error_free_data; + } + + data->f01_container = fc; + + return retval; + +error_free_data: + kfree(fc); + return retval; +} + +/* + * Scan the PDT for F01 so we can force a reset before anything else + * is done. This forces the sensor into a known state, and also + * forces application of any pending updates from reflashing the + * firmware or configuration. We have to do this before actually + * building the PDT because the reflash might cause various registers + * to move around. + */ +static int do_initial_reset(struct rmi_device* rmi_dev) +{ + struct pdt_entry pdt_entry; + int page; + struct device *dev = &rmi_dev->dev; + bool done = false; + int i; + int retval; + + pr_info("in function ____%s____ \n", __func__); + + polled_synaptics_rmi_device = rmi_dev; + + for (page = 0; (page <= RMI4_MAX_PAGE) && !done; page++) { + + u16 page_start = RMI4_PAGE_SIZE * page; + u16 pdt_start = page_start + PDT_START_SCAN_LOCATION; + u16 pdt_end = page_start + PDT_END_SCAN_LOCATION; + pr_info(" reading page = %d\n", page ); + done = true; + for (i = pdt_start; i >= pdt_end; i -= sizeof(pdt_entry)) { + + pr_info(" reading PDT entry %3d (block %3d)\n", + i%sizeof(pdt_entry), i); + + retval = rmi_read_block(rmi_dev, i, (u8 *)&pdt_entry, + sizeof(pdt_entry)); + if (retval != sizeof(pdt_entry)) { + dev_err(dev, "Read PDT entry at 0x%04x" + "failed, code = %d.\n", i, retval); + return retval; + } + + if (RMI4_END_OF_PDT(pdt_entry.function_number)) + break; + done = false; + + if (pdt_entry.function_number == 0x01) { + u16 cmd_addr = page_start + + pdt_entry.command_base_addr; + u8 cmd_buf = RMI_DEVICE_RESET_CMD; + retval = rmi_write_block(rmi_dev, cmd_addr, + &cmd_buf, 1); + if (retval < 0) { + dev_err(dev, "Initial reset failed. " + "Code = %d.\n", retval); + return retval; + } + mdelay(INITIAL_RESET_WAIT_MS); + return 0; + } + } + } + + dev_warn(dev, "WARNING: Failed to find F01 for initial reset.\n"); + return -ENODEV; +} + + +static int rmi_scan_pdt(struct rmi_device *rmi_dev) +{ + struct rmi_driver_data *data; + struct rmi_function_container *fc; + struct pdt_entry pdt_entry; + int page; + struct device *dev = &rmi_dev->dev; + int irq_count = 0; + bool done = false; + int i; + int retval; + pr_info("in function ____%s____ \n", __func__); + pr_info(" doing initial reset \n"); + + retval = do_initial_reset(rmi_dev); + pr_info(" back in %s \n", __func__); + + if (retval) + dev_err(dev, "WARNING: Initial reset failed! Soldiering on.\n"); + + data = rmi_get_driverdata(rmi_dev); + + INIT_LIST_HEAD(&data->rmi_functions.list); + + /* parse the PDT */ + for (page = 0; (page <= RMI4_MAX_PAGE) && !done; page++) { + u16 page_start = RMI4_PAGE_SIZE * page; + u16 pdt_start = page_start + PDT_START_SCAN_LOCATION; + u16 pdt_end = page_start + PDT_END_SCAN_LOCATION; + + done = true; + for (i = pdt_start; i >= pdt_end; i -= sizeof(pdt_entry)) { + retval = rmi_read_block(rmi_dev, i, (u8 *)&pdt_entry, + sizeof(pdt_entry)); + if (retval != sizeof(pdt_entry)) { + dev_err(dev, "Read PDT entry at 0x%04x" + "failed.\n", i); + goto error_exit; + } + + if (RMI4_END_OF_PDT(pdt_entry.function_number)) + break; + + dev_dbg(dev, "%s: Found F%.2X on page 0x%02X\n", + __func__, pdt_entry.function_number, page); + done = false; + + /* + * F01 is handled by rmi_driver. Hopefully we will get + * rid of the special treatment of f01 at some point + * in time. + */ + if (pdt_entry.function_number == 0x01) { + retval = rmi_driver_fn_01_specific(rmi_dev, + &pdt_entry, &irq_count, + page_start); + if (retval) + goto error_exit; + continue; + } + + fc = kzalloc(sizeof(struct rmi_function_container), + GFP_KERNEL); + if (!fc) { + dev_err(dev, "Failed to allocate function " + "container for F%02X.\n", + pdt_entry.function_number); + retval = -ENOMEM; + goto error_exit; + } + + copy_pdt_entry_to_fd(&pdt_entry, &fc->fd, page_start); + + fc->rmi_dev = rmi_dev; + fc->num_of_irqs = pdt_entry.interrupt_source_count; + fc->irq_pos = irq_count; + irq_count += fc->num_of_irqs; + + retval = init_one_function(rmi_dev, fc); + if (retval < 0) { + dev_err(dev, "Failed to initialize F%.2x\n", + pdt_entry.function_number); + kfree(fc); + goto error_exit; + } + + list_add_tail(&fc->list, &data->rmi_functions.list); + } + } + data->num_of_irq_regs = (irq_count + 7) / 8; + dev_dbg(dev, "%s: Done with PDT scan.\n", __func__); + return 0; + +error_exit: + return retval; +} + + +#ifdef SYNAPTICS_SENSOR_POLL +void synaptics_sensor_poller(unsigned long data){ + pr_info("in function ____%s____ , rmi_device= 0x%8x \n", __func__, polled_synaptics_rmi_device); + // msleep(10000); + for (;;) { + msleep(100); + rmi_driver_irq_handler(polled_synaptics_rmi_device, 0); + } + + return; +} + +struct workqueue_struct *synaptics_rmi_polling_queue = NULL; +struct delayed_work synaptics_rmi_polling_work; + +#endif + + +static int rmi_driver_probe(struct rmi_device *rmi_dev) +{ + struct rmi_driver_data *data; + struct rmi_function_container *fc; + struct rmi_device_platform_data *pdata; + int error = 0; + struct device *dev = &rmi_dev->dev; + int attr_count = 0; + + dev_dbg(dev, "%s: Starting probe.\n", __func__); + + pdata = to_rmi_platform_data(rmi_dev); + + data = kzalloc(sizeof(struct rmi_driver_data), GFP_KERNEL); + if (!data) { + dev_err(dev, "%s: Failed to allocate driver data.\n", __func__); + return -ENOMEM; + } + + rmi_set_driverdata(rmi_dev, data); + + error = rmi_scan_pdt(rmi_dev); + if (error) { + dev_err(dev, "PDT scan failed with code %d.\n", error); + goto err_free_data; + } + + if (!data->f01_container) { + dev_err(dev, "missing f01 function!\n"); + error = -EINVAL; + goto err_free_data; + } + + data->f01_container->irq_mask = kzalloc( + sizeof(u8) * data->num_of_irq_regs, GFP_KERNEL); + if (!data->f01_container->irq_mask) { + dev_err(dev, "Failed to allocate F01 IRQ mask.\n"); + error = -ENOMEM; + goto err_free_data; + } + construct_mask(data->f01_container->irq_mask, + data->f01_container->num_of_irqs, + data->f01_container->irq_pos); + list_for_each_entry(fc, &data->rmi_functions.list, list) + fc->irq_mask = rmi_driver_irq_get_mask(rmi_dev, fc); + + error = rmi_driver_f01_init(rmi_dev); + if (error < 0) { + dev_err(dev, "Failed to initialize F01.\n"); + goto err_free_data; + } + + error = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION, + (char *) &data->pdt_props); + if (error < 0) { + /* we'll print out a warning and continue since + * failure to get the PDT properties is not a cause to fail + */ + dev_warn(dev, "Could not read PDT properties from 0x%04x. " + "Assuming 0x00.\n", PDT_PROPERTIES_LOCATION); + } + + dev_dbg(dev, "%s: Creating sysfs files.", __func__); + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + error = device_create_file(dev, &attrs[attr_count]); + if (error < 0) { + dev_err(dev, "%s: Failed to create sysfs file %s.\n", + __func__, attrs[attr_count].attr.name); + goto err_free_data; + } + } + + __mutex_init(&data->irq_mutex, "irq_mutex", &data->irq_key); + data->current_irq_mask = kzalloc(sizeof(u8) * data->num_of_irq_regs, + GFP_KERNEL); + if (!data->current_irq_mask) { + dev_err(dev, "Failed to allocate current_irq_mask.\n"); + error = -ENOMEM; + goto err_free_data; + } + error = rmi_read_block(rmi_dev, + data->f01_container->fd.control_base_addr+1, + data->current_irq_mask, data->num_of_irq_regs); + if (error < 0) { + dev_err(dev, "%s: Failed to read current IRQ mask.\n", + __func__); + goto err_free_data; + } + data->irq_mask_store = kzalloc(sizeof(u8) * data->num_of_irq_regs, + GFP_KERNEL); + if (!data->irq_mask_store) { + dev_err(dev, "Failed to allocate mask store.\n"); + error = -ENOMEM; + goto err_free_data; + } + +#if defined(CONFIG_RMI4_DEV) + if (rmi_char_dev_register(rmi_dev->phys)) + pr_err("%s: error register char device", __func__); +#endif /*CONFIG_RMI4_DEV*/ + +#ifdef CONFIG_PM + data->pm_data = pdata->pm_data; + data->pre_suspend = pdata->pre_suspend; + data->post_resume = pdata->post_resume; + + mutex_init(&data->suspend_mutex); + +#ifdef CONFIG_HAS_EARLYSUSPEND + rmi_dev->early_suspend_handler.level = + EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + rmi_dev->early_suspend_handler.suspend = rmi_driver_early_suspend; + rmi_dev->early_suspend_handler.resume = rmi_driver_late_resume; + register_early_suspend(&rmi_dev->early_suspend_handler); +#endif /* CONFIG_HAS_EARLYSUSPEND */ +#endif /* CONFIG_PM */ + data->enabled = true; + + dev_info(dev, "connected RMI device manufacturer: %s product: %s\n", + data->manufacturer_id == 1 ? "synaptics" : "unknown", + data->product_id); + +#ifdef SYNAPTICS_SENSOR_POLL + synaptics_rmi_polling_queue = create_singlethread_workqueue("rmi_poll_work"); + INIT_DELAYED_WORK_DEFERRABLE(&synaptics_rmi_polling_work, synaptics_sensor_poller); + pr_info("%s: setting up POLLING mode, rmi_device= 0x%8x \n", __func__, polled_synaptics_rmi_device); + queue_delayed_work(synaptics_rmi_polling_queue, &synaptics_rmi_polling_work, 1000); +#endif + return 0; + + err_free_data: + rmi_free_function_list(rmi_dev); + for (attr_count--; attr_count >= 0; attr_count--) + device_remove_file(dev, &attrs[attr_count]); + kfree(data->f01_container->irq_mask); + kfree(data->irq_mask_store); + kfree(data->current_irq_mask); + kfree(data); + return error; +} + +#ifdef CONFIG_PM +static int rmi_driver_suspend(struct device *dev) +{ + struct rmi_device *rmi_dev; + struct rmi_driver_data *data; + struct rmi_function_container *entry; + int retval = 0; + + rmi_dev = to_rmi_device(dev); + data = rmi_get_driverdata(rmi_dev); + + mutex_lock(&data->suspend_mutex); + if (data->suspended) + goto exit; + + if (data->pre_suspend) { + retval = data->pre_suspend(data->pm_data); + if (retval) + goto exit; + } + + list_for_each_entry(entry, &data->rmi_functions.list, list) + if (entry->fh && entry->fh->suspend) { + retval = entry->fh->suspend(entry); + if (retval < 0) + goto exit; + } + + if (data->f01_container && data->f01_container->fh + && data->f01_container->fh->suspend) { + retval = data->f01_container->fh->suspend(data->f01_container); + if (retval < 0) + goto exit; + } + data->suspended = true; + +exit: + mutex_unlock(&data->suspend_mutex); + return retval; +} + +static int rmi_driver_resume(struct device *dev) +{ + struct rmi_device *rmi_dev; + struct rmi_driver_data *data; + struct rmi_function_container *entry; + int retval = 0; + + rmi_dev = to_rmi_device(dev); + data = rmi_get_driverdata(rmi_dev); + + mutex_lock(&data->suspend_mutex); + if (!data->suspended) + goto exit; + + if (data->f01_container && data->f01_container->fh + && data->f01_container->fh->resume) { + retval = data->f01_container->fh->resume(data->f01_container); + if (retval < 0) + goto exit; + } + + list_for_each_entry(entry, &data->rmi_functions.list, list) + if (entry->fh && entry->fh->resume) { + retval = entry->fh->resume(entry); + if (retval < 0) + goto exit; + } + + if (data->post_resume) { + retval = data->post_resume(data->pm_data); + if (retval) + goto exit; + } + + data->suspended = false; + +exit: + mutex_unlock(&data->suspend_mutex); + return retval; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void rmi_driver_early_suspend(struct early_suspend *h) +{ + struct rmi_device *rmi_dev = + container_of(h, struct rmi_device, early_suspend_handler); + + dev_dbg(&rmi_dev->dev, "Early suspend.\n"); + rmi_driver_suspend(&rmi_dev->dev); +} + +static void rmi_driver_late_resume(struct early_suspend *h) +{ + struct rmi_device *rmi_dev = + container_of(h, struct rmi_device, early_suspend_handler); + + dev_dbg(&rmi_dev->dev, "Late resume.\n"); + rmi_driver_resume(&rmi_dev->dev); +} +#endif /* CONFIG_HAS_EARLYSUSPEND */ +#endif /* CONFIG_PM */ + +static int rmi_driver_remove(struct rmi_device *rmi_dev) +{ + struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev); + struct rmi_function_container *entry; + int i; + + list_for_each_entry(entry, &data->rmi_functions.list, list) + if (entry->fh && entry->fh->remove) + entry->fh->remove(entry); + + rmi_free_function_list(rmi_dev); + for (i = 0; i < ARRAY_SIZE(attrs); i++) + device_remove_file(&rmi_dev->dev, &attrs[i]); + kfree(data->f01_container->irq_mask); + kfree(data->irq_mask_store); + kfree(data->current_irq_mask); + kfree(data); + + return 0; +} + +#ifdef UNIVERSAL_DEV_PM_OPS +static UNIVERSAL_DEV_PM_OPS(rmi_driver_pm, rmi_driver_suspend, + rmi_driver_resume, NULL); +#endif + +static struct rmi_driver sensor_driver = { + .driver = { + .owner = THIS_MODULE, + // .name = "rmi-generic", + .name = "rmi_generic", +#ifdef UNIVERSAL_DEV_PM_OPS + .pm = &rmi_driver_pm, +#endif + }, + .probe = rmi_driver_probe, + .irq_handler = rmi_driver_irq_handler, + .fh_add = rmi_driver_fh_add, + .fh_remove = rmi_driver_fh_remove, + .get_func_irq_mask = rmi_driver_irq_get_mask, + .store_irq_mask = rmi_driver_irq_save, + .restore_irq_mask = rmi_driver_irq_restore, + .remove = rmi_driver_remove +}; + +/* Utility routine to handle writes to read-only attributes. Hopefully + * this will never happen, but if the user does something stupid, we don't + * want to accept it quietly (which is what can happen if you just put NULL + * for the attribute's store function). + */ +ssize_t rmi_store_error(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + dev_warn(dev, + "RMI4 WARNING: Attempt to write %d characters to read-only " + "attribute %s.", count, attr->attr.name); + return -EPERM; +} + +/* Utility routine to handle reads of write-only attributes. Hopefully + * this will never happen, but if the user does something stupid, we don't + * want to accept it quietly (which is what can happen if you just put NULL + * for the attribute's show function). + */ +ssize_t rmi_show_error(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + dev_warn(dev, + "RMI4 WARNING: Attempt to read from write-only attribute %s.", + attr->attr.name); + return -EPERM; +} + +/* sysfs show and store fns for driver attributes */ +static ssize_t rmi_driver_hasbsr_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_device *rmi_dev; + struct rmi_driver_data *data; + rmi_dev = to_rmi_device(dev); + data = rmi_get_driverdata(rmi_dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", has_bsr(data)); +} + +static ssize_t rmi_driver_bsr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rmi_device *rmi_dev; + struct rmi_driver_data *data; + rmi_dev = to_rmi_device(dev); + data = rmi_get_driverdata(rmi_dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", data->bsr); +} + +static ssize_t rmi_driver_bsr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int retval; + unsigned long val; + struct rmi_device *rmi_dev; + struct rmi_driver_data *data; + + rmi_dev = to_rmi_device(dev); + data = rmi_get_driverdata(rmi_dev); + + /* need to convert the string data to an actual value */ + retval = strict_strtoul(buf, 10, &val); + if (retval < 0) { + dev_err(dev, "Invalid value '%s' written to BSR.\n", buf); + return -EINVAL; + } + + retval = rmi_write(rmi_dev, BSR_LOCATION, (unsigned char)val); + if (retval) { + dev_err(dev, "%s : failed to write bsr %u to 0x%x\n", + __func__, (unsigned int)val, BSR_LOCATION); + return retval; + } + + data->bsr = val; + + return count; +} + +static void disable_sensor(struct rmi_device *rmi_dev) +{ + struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev); + + rmi_dev->phys->disable_device(rmi_dev->phys); + + data->enabled = false; +} + +static int enable_sensor(struct rmi_device *rmi_dev) +{ + struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev); + int retval = 0; + pr_info("in function ____%s____ \n", __func__); + retval = rmi_dev->phys->enable_device(rmi_dev->phys); + /* non-zero means error occurred */ + if (retval) + return retval; + + data->enabled = true; + + return 0; +} + +static ssize_t rmi_driver_enabled_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rmi_device *rmi_dev; + struct rmi_driver_data *data; + + rmi_dev = to_rmi_device(dev); + data = rmi_get_driverdata(rmi_dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", data->enabled); +} + +static ssize_t rmi_driver_enabled_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int retval; + int new_value; + struct rmi_device *rmi_dev; + struct rmi_driver_data *data; + + rmi_dev = to_rmi_device(dev); + data = rmi_get_driverdata(rmi_dev); + + if (sysfs_streq(buf, "0")) + new_value = false; + else if (sysfs_streq(buf, "1")) + new_value = true; + else + return -EINVAL; + + if (new_value) { + retval = enable_sensor(rmi_dev); + if (retval) { + dev_err(dev, "Failed to enable sensor, code=%d.\n", + retval); + return -EIO; + } + } else { + disable_sensor(rmi_dev); + } + + return count; +} + +#if REGISTER_DEBUG +static ssize_t rmi_driver_reg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int retval; + unsigned int address; + unsigned int bytes; + struct rmi_device *rmi_dev; + struct rmi_driver_data *data; + u8 readbuf[128]; + unsigned char outbuf[512]; + unsigned char *bufptr = outbuf; + int i; + + rmi_dev = to_rmi_device(dev); + data = rmi_get_driverdata(rmi_dev); + + retval = sscanf(buf, "%x %u", &address, &bytes); + if (retval != 2) { + dev_err(dev, "Invalid input (code %d) for reg store: %s", + retval, buf); + return -EINVAL; + } + if (address < 0 || address > 0xFFFF) { + dev_err(dev, "Invalid address for reg store '%#06x'.\n", + address); + return -EINVAL; + } + if (bytes < 0 || bytes >= sizeof(readbuf) || address+bytes > 65535) { + dev_err(dev, "Invalid byte count for reg store '%d'.\n", + bytes); + return -EINVAL; + } + + retval = rmi_read_block(rmi_dev, address, readbuf, bytes); + if (retval != bytes) { + dev_err(dev, "Failed to read %d registers at %#06x, code %d.\n", + bytes, address, retval); + return retval; + } + + dev_info(dev, "Reading %d bytes from %#06x.\n", bytes, address); + for (i = 0; i < bytes; i++) { + retval = snprintf(bufptr, 4, "%02X ", readbuf[i]); + if (retval < 0) { + dev_err(dev, "Failed to format string. Code: %d", + retval); + return retval; + } + bufptr += retval; + } + dev_info(dev, "%s\n", outbuf); + + return count; +} +#endif + +#if DELAY_DEBUG +static ssize_t rmi_delay_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int retval; + struct rmi_device *rmi_dev; + struct rmi_device_platform_data *pdata; + unsigned int new_read_delay; + unsigned int new_write_delay; + unsigned int new_block_delay; + unsigned int new_pre_delay; + unsigned int new_post_delay; + + retval = sscanf(buf, "%u %u %u %u %u", &new_read_delay, + &new_write_delay, &new_block_delay, + &new_pre_delay, &new_post_delay); + if (retval != 5) { + dev_err(dev, "Incorrect number of values provided for delay."); + return -EINVAL; + } + if (new_read_delay < 0) { + dev_err(dev, "Byte delay must be positive microseconds.\n"); + return -EINVAL; + } + if (new_write_delay < 0) { + dev_err(dev, "Write delay must be positive microseconds.\n"); + return -EINVAL; + } + if (new_block_delay < 0) { + dev_err(dev, "Block delay must be positive microseconds.\n"); + return -EINVAL; + } + if (new_pre_delay < 0) { + dev_err(dev, + "Pre-transfer delay must be positive microseconds.\n"); + return -EINVAL; + } + if (new_post_delay < 0) { + dev_err(dev, + "Post-transfer delay must be positive microseconds.\n"); + return -EINVAL; + } + + rmi_dev = to_rmi_device(dev); + pdata = rmi_dev->phys->dev->platform_data; + + dev_info(dev, "Setting delays to %u %u %u %u %u.\n", new_read_delay, + new_write_delay, new_block_delay, new_pre_delay, + new_post_delay); + pdata->spi_data.read_delay_us = new_read_delay; + pdata->spi_data.write_delay_us = new_write_delay; + pdata->spi_data.block_delay_us = new_block_delay; + pdata->spi_data.pre_delay_us = new_pre_delay; + pdata->spi_data.post_delay_us = new_post_delay; + + return count; +} + +static ssize_t rmi_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rmi_device *rmi_dev; + struct rmi_device_platform_data *pdata; + + rmi_dev = to_rmi_device(dev); + pdata = rmi_dev->phys->dev->platform_data; + + return snprintf(buf, PAGE_SIZE, "%d %d %d %d %d\n", + pdata->spi_data.read_delay_us, pdata->spi_data.write_delay_us, + pdata->spi_data.block_delay_us, + pdata->spi_data.pre_delay_us, pdata->spi_data.post_delay_us); +} +#endif + +static ssize_t rmi_driver_phys_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rmi_device *rmi_dev; + struct rmi_phys_info *info; + + rmi_dev = to_rmi_device(dev); + info = &rmi_dev->phys->info; + + return snprintf(buf, PAGE_SIZE, "%-5s %ld %ld %ld %ld %ld %ld %ld\n", + info->proto ? info->proto : "unk", + info->tx_count, info->tx_bytes, info->tx_errs, + info->rx_count, info->rx_bytes, info->rx_errs, + info->attn_count); +} + +static ssize_t rmi_driver_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", + RMI_DRIVER_VERSION_STRING); +} + +static int __init rmi_driver_init(void) +{ + return rmi_register_driver(&sensor_driver); +} + +static void __exit rmi_driver_exit(void) +{ + rmi_unregister_driver(&sensor_driver); +} + +module_init(rmi_driver_init); +module_exit(rmi_driver_exit); + +MODULE_AUTHOR("Eric Andersson "); +MODULE_DESCRIPTION("RMI generic driver"); diff --git a/drivers/input/touchscreen/rmi4/rmi_driver.h b/drivers/input/touchscreen/rmi4/rmi_driver.h new file mode 100644 index 000000000000..9e9f2c098225 --- /dev/null +++ b/drivers/input/touchscreen/rmi4/rmi_driver.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2011 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef _RMI_DRIVER_H +#define _RMI_DRIVER_H + +#define RMI_DRIVER_MAJOR_VERSION 1 +#define RMI_DRIVER_MINOR_VERSION 3 +#define RMI_DRIVER_SUB_MINOR_VERSION 0 + +/* TODO: Figure out some way to construct this string in the define macro + * using the values defined above. + */ +#define RMI_DRIVER_VERSION_STRING "1.3.0" + + +#define RMI_PRODUCT_ID_LENGTH 10 +#define RMI_PRODUCT_INFO_LENGTH 2 +#define RMI_DATE_CODE_LENGTH 3 + +struct rmi_driver_data { + struct rmi_function_container rmi_functions; + + struct rmi_function_container *f01_container; + + int num_of_irq_regs; + u8 *current_irq_mask; + u8 *irq_mask_store; + bool irq_stored; + struct mutex irq_mutex; + struct lock_class_key irq_key; + + unsigned char pdt_props; + unsigned char bsr; + bool enabled; + + u8 manufacturer_id; + /* product id + null termination */ + u8 product_id[RMI_PRODUCT_ID_LENGTH + 1]; + +#ifdef CONFIG_PM + bool suspended; + struct mutex suspend_mutex; + + void *pm_data; + int (*pre_suspend) (const void *pm_data); + int (*post_resume) (const void *pm_data); +#endif + + void *data; +}; + +struct pdt_entry { + u8 query_base_addr:8; + u8 command_base_addr:8; + u8 control_base_addr:8; + u8 data_base_addr:8; + u8 interrupt_source_count:3; + u8 bits3and4:2; + u8 function_version:2; + u8 bit7:1; + u8 function_number:8; +}; + +int rmi_driver_f01_init(struct rmi_device *rmi_dev); + +static inline void copy_pdt_entry_to_fd(struct pdt_entry *pdt, + struct rmi_function_descriptor *fd, + u16 page_start) +{ + fd->query_base_addr = pdt->query_base_addr + page_start; + fd->command_base_addr = pdt->command_base_addr + page_start; + fd->control_base_addr = pdt->control_base_addr + page_start; + fd->data_base_addr = pdt->data_base_addr + page_start; + fd->function_number = pdt->function_number; + fd->interrupt_source_count = pdt->interrupt_source_count; + fd->function_version = pdt->function_version; +} + +/* Helper function to convert a short (in host processor endianess) to + * a byte array in the RMI endianess for shorts. See above comment for + * why we dont us htons or something like that. + */ +void hstoba(u8 *dest, u16 src); + +/* Helper fn to convert a byte array representing a short in the RMI + * endian-ness to a short in the native processor's specific endianness. + * We don't use ntohs/htons here because, well, we're not dealing with + * a pair of shorts. And casting dest to short* wouldn't work, because + * that would imply knowing the byte order of short in the first place. + */ +void batohs(u16 *dest, u8 *src); + +#endif diff --git a/drivers/input/touchscreen/rmi4/rmi_f01.c b/drivers/input/touchscreen/rmi4/rmi_f01.c new file mode 100644 index 000000000000..ef9adb0586b9 --- /dev/null +++ b/drivers/input/touchscreen/rmi4/rmi_f01.c @@ -0,0 +1,775 @@ +/* + * Copyright (c) 2011 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * 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. + */ + +#define DEBUG + +#include +#include +#include +#include "rmi_driver.h" + +/* control register bits */ +#define RMI_SLEEP_MODE_NORMAL (0x00) +#define RMI_SLEEP_MODE_SENSOR_SLEEP (0x01) +#define RMI_SLEEP_MODE_RESERVED0 (0x02) +#define RMI_SLEEP_MODE_RESERVED1 (0x03) + +#define RMI_IS_VALID_SLEEPMODE(mode) \ + (mode >= RMI_SLEEP_MODE_NORMAL && mode <= RMI_SLEEP_MODE_RESERVED1) + +union f01_device_commands { + struct { + u8 reset:1; + u8 reserved:1; + }; + u8 reg; +}; + +union f01_device_control { + struct { + u8 sleep_mode:2; + u8 nosleep:1; + u8 reserved:2; + u8 charger_input:1; + u8 report_rate:1; + u8 configured:1; + }; + u8 reg; +}; + +union f01_device_status { + struct { + u8 status_code:4; + u8 reserved:2; + u8 flash_prog:1; + u8 unconfigured:1; + }; + u8 reg; +}; + +union f01_basic_queries { + struct { + u8 manufacturer_id:8; + + u8 custom_map:1; + u8 non_compliant:1; + u8 q1_bit_2:1; + u8 has_sensor_id:1; + u8 has_charger_input:1; + u8 has_adjustable_doze:1; + u8 has_adjustable_doze_holdoff:1; + u8 q1_bit_7:1; + + u8 productinfo_1:7; + u8 q2_bit_7:1; + u8 productinfo_2:7; + u8 q3_bit_7:1; + + u8 year:5; + u8 month:4; + u8 day:5; + u8 cp1:1; + u8 cp2:1; + u8 wafer_id1_lsb:8; + u8 wafer_id1_msb:8; + u8 wafer_id2_lsb:8; + u8 wafer_id2_msb:8; + u8 wafer_id3_lsb:8; + }; + u8 regs[11]; +}; + +struct f01_data { + union f01_device_control device_control; + union f01_basic_queries basic_queries; + union f01_device_status device_status; + u8 product_id[RMI_PRODUCT_ID_LENGTH+1]; + +#ifdef CONFIG_PM + bool suspended; + bool old_nosleep; +#endif +}; + + +static ssize_t rmi_fn_01_productinfo_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_fn_01_productid_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_fn_01_manufacturer_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_fn_01_datecode_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_fn_01_reportrate_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_fn_01_reportrate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static ssize_t rmi_fn_01_reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static ssize_t rmi_fn_01_sleepmode_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_fn_01_sleepmode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static ssize_t rmi_fn_01_nosleep_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_fn_01_nosleep_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static ssize_t rmi_fn_01_chargerinput_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_fn_01_chargerinput_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static ssize_t rmi_fn_01_configured_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_fn_01_unconfigured_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_fn_01_flashprog_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_fn_01_statuscode_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static struct device_attribute fn_01_attrs[] = { + __ATTR(productinfo, RMI_RO_ATTR, + rmi_fn_01_productinfo_show, rmi_store_error), + __ATTR(productid, RMI_RO_ATTR, + rmi_fn_01_productid_show, rmi_store_error), + __ATTR(manufacturer, RMI_RO_ATTR, + rmi_fn_01_manufacturer_show, rmi_store_error), + __ATTR(datecode, RMI_RO_ATTR, + rmi_fn_01_datecode_show, rmi_store_error), + + /* control register access */ + __ATTR(sleepmode, RMI_RW_ATTR, + rmi_fn_01_sleepmode_show, rmi_fn_01_sleepmode_store), + __ATTR(nosleep, RMI_RW_ATTR, + rmi_fn_01_nosleep_show, rmi_fn_01_nosleep_store), + __ATTR(chargerinput, RMI_RW_ATTR, + rmi_fn_01_chargerinput_show, rmi_fn_01_chargerinput_store), + __ATTR(reportrate, RMI_RW_ATTR, + rmi_fn_01_reportrate_show, rmi_fn_01_reportrate_store), + /* We make report rate RO, since the driver uses that to look for + * resets. We don't want someone faking us out by changing that + * bit. + */ + __ATTR(configured, RMI_RO_ATTR, + rmi_fn_01_configured_show, rmi_store_error), + + /* Command register access. */ + __ATTR(reset, RMI_WO_ATTR, + rmi_show_error, rmi_fn_01_reset_store), + + /* STatus register access. */ + __ATTR(unconfigured, RMI_RO_ATTR, + rmi_fn_01_unconfigured_show, rmi_store_error), + __ATTR(flashprog, RMI_RO_ATTR, + rmi_fn_01_flashprog_show, rmi_store_error), + __ATTR(statuscode, RMI_RO_ATTR, + rmi_fn_01_statuscode_show, rmi_store_error), +}; + +/* Utility routine to set the value of a bit field in a register. */ +int rmi_set_bit_field(struct rmi_device *rmi_dev, + unsigned short address, + unsigned char field_mask, + unsigned char bits) +{ + unsigned char reg_contents; + int retval; + + retval = rmi_read(rmi_dev, address, ®_contents); + if (retval) + return retval; + reg_contents = (reg_contents & ~field_mask) | bits; + retval = rmi_write(rmi_dev, address, reg_contents); + if (retval == 1) + return 0; + else if (retval == 0) + return -EIO; + return retval; +} + +static ssize_t rmi_fn_01_productinfo_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct f01_data *data = NULL; + struct rmi_function_container *fc = to_rmi_function_container(dev); + + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "0x%02x 0x%02x\n", + data->basic_queries.productinfo_1, + data->basic_queries.productinfo_2); +} + +static ssize_t rmi_fn_01_productid_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct f01_data *data = NULL; + struct rmi_function_container *fc = to_rmi_function_container(dev); + + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%s\n", data->product_id); +} + +static ssize_t rmi_fn_01_manufacturer_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct f01_data *data = NULL; + struct rmi_function_container *fc = to_rmi_function_container(dev); + + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "0x%02x\n", + data->basic_queries.manufacturer_id); +} + +static ssize_t rmi_fn_01_datecode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct f01_data *data = NULL; + struct rmi_function_container *fc = to_rmi_function_container(dev); + + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "20%02u-%02u-%02u\n", + data->basic_queries.year, + data->basic_queries.month, + data->basic_queries.day); +} + +static ssize_t rmi_fn_01_reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rmi_function_container *fc = NULL; + unsigned int reset; + int retval = 0; + /* Command register always reads as 0, so we can just use a local. */ + union f01_device_commands commands = {}; + + fc = to_rmi_function_container(dev); + + if (sscanf(buf, "%u", &reset) != 1) + return -EINVAL; + if (reset < 0 || reset > 1) + return -EINVAL; + + /* Per spec, 0 has no effect, so we skip it entirely. */ + if (reset) { + commands.reset = 1; + retval = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr, + &commands.reg, sizeof(commands.reg)); + if (retval < 0) { + dev_err(dev, "%s: failed to issue reset command, " + "error = %d.", __func__, retval); + return retval; + } + } + + return count; +} + +static ssize_t rmi_fn_01_sleepmode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct f01_data *data = NULL; + struct rmi_function_container *fc = to_rmi_function_container(dev); + + data = fc->data; + + return snprintf(buf, PAGE_SIZE, + "%d\n", data->device_control.sleep_mode); +} + +static ssize_t rmi_fn_01_sleepmode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct f01_data *data = NULL; + unsigned long new_value; + int retval; + struct rmi_function_container *fc = to_rmi_function_container(dev); + + data = fc->data; + + retval = strict_strtoul(buf, 10, &new_value); + if (retval < 0 || !RMI_IS_VALID_SLEEPMODE(new_value)) { + dev_err(dev, "%s: Invalid sleep mode %s.", __func__, buf); + return -EINVAL; + } + + dev_dbg(dev, "Setting sleep mode to %ld.", new_value); + data->device_control.sleep_mode = new_value; + retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr, + &data->device_control.reg, + sizeof(data->device_control.reg)); + if (retval >= 0) + retval = count; + else + dev_err(dev, "Failed to write sleep mode, code %d.\n", retval); + return retval; +} + +static ssize_t rmi_fn_01_nosleep_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct f01_data *data = NULL; + struct rmi_function_container *fc = to_rmi_function_container(dev); + + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%d\n", data->device_control.nosleep); +} + +static ssize_t rmi_fn_01_nosleep_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct f01_data *data = NULL; + unsigned long new_value; + int retval; + struct rmi_function_container *fc = to_rmi_function_container(dev); + + data = fc->data; + + retval = strict_strtoul(buf, 10, &new_value); + if (retval < 0 || new_value < 0 || new_value > 1) { + dev_err(dev, "%s: Invalid nosleep bit %s.", __func__, buf); + return -EINVAL; + } + + data->device_control.nosleep = new_value; + retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr, + &data->device_control.reg, + sizeof(data->device_control.reg)); + if (retval >= 0) + retval = count; + else + dev_err(dev, "Failed to write nosleep bit.\n"); + return retval; +} + +static ssize_t rmi_fn_01_chargerinput_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct f01_data *data = NULL; + struct rmi_function_container *fc = to_rmi_function_container(dev); + + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%d\n", + data->device_control.charger_input); +} + +static ssize_t rmi_fn_01_chargerinput_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct f01_data *data = NULL; + unsigned long new_value; + int retval; + struct rmi_function_container *fc = to_rmi_function_container(dev); + + data = fc->data; + + retval = strict_strtoul(buf, 10, &new_value); + if (retval < 0 || new_value < 0 || new_value > 1) { + dev_err(dev, "%s: Invalid chargerinput bit %s.", __func__, buf); + return -EINVAL; + } + + data->device_control.charger_input = new_value; + retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr, + &data->device_control.reg, + sizeof(data->device_control.reg)); + if (retval >= 0) + retval = count; + else + dev_err(dev, "Failed to write chargerinput bit.\n"); + return retval; +} + +static ssize_t rmi_fn_01_reportrate_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct f01_data *data = NULL; + struct rmi_function_container *fc = to_rmi_function_container(dev); + + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%d\n", + data->device_control.report_rate); +} + +static ssize_t rmi_fn_01_reportrate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct f01_data *data = NULL; + unsigned long new_value; + int retval; + struct rmi_function_container *fc = to_rmi_function_container(dev); + + data = fc->data; + + retval = strict_strtoul(buf, 10, &new_value); + if (retval < 0 || new_value < 0 || new_value > 1) { + dev_err(dev, "%s: Invalid reportrate bit %s.", __func__, buf); + return -EINVAL; + } + + data->device_control.report_rate = new_value; + retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr, + &data->device_control.reg, + sizeof(data->device_control.reg)); + if (retval >= 0) + retval = count; + else + dev_err(dev, "Failed to write reportrate bit.\n"); + return retval; +} + +static ssize_t rmi_fn_01_configured_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct f01_data *data = NULL; + struct rmi_function_container *fc = to_rmi_function_container(dev); + + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%d\n", + data->device_control.configured); +} + +static ssize_t rmi_fn_01_unconfigured_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct f01_data *data = NULL; + struct rmi_function_container *fc = to_rmi_function_container(dev); + + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%d\n", + data->device_status.unconfigured); +} + +static ssize_t rmi_fn_01_flashprog_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct f01_data *data = NULL; + struct rmi_function_container *fc = to_rmi_function_container(dev); + + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%d\n", + data->device_status.flash_prog); +} + +static ssize_t rmi_fn_01_statuscode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct f01_data *data = NULL; + struct rmi_function_container *fc = to_rmi_function_container(dev); + + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "0x%02x\n", + data->device_status.status_code); +} + +int rmi_driver_f01_init(struct rmi_device *rmi_dev) +{ + struct rmi_driver_data *driver_data = rmi_get_driverdata(rmi_dev); + struct rmi_function_container *fc = driver_data->f01_container; + struct f01_data *data; + int error; + u8 temp; + int attr_count; + + data = kzalloc(sizeof(struct f01_data), GFP_KERNEL); + if (!data) { + dev_err(&rmi_dev->dev, "Failed to allocate F01 data.\n"); + return -ENOMEM; + } + fc->data = data; + + /* Set the configured bit. */ + error = rmi_read_block(rmi_dev, fc->fd.control_base_addr, + &data->device_control.reg, + sizeof(data->device_control.reg)); + if (error < 0) { + dev_err(&fc->dev, "Failed to read F01 control.\n"); + goto error_exit; + } + + /* Sleep mode might be set as a hangover from a system crash or + * reboot without power cycle. If so, clear it so the sensor + * is certain to function. + */ + if (data->device_control.sleep_mode != RMI_SLEEP_MODE_NORMAL) { + dev_warn(&fc->dev, + "WARNING: Non-zero sleep mode found. Clearing...\n"); + data->device_control.sleep_mode = RMI_SLEEP_MODE_NORMAL; + } + + data->device_control.configured = 1; + error = rmi_write_block(rmi_dev, fc->fd.control_base_addr, + &data->device_control.reg, + sizeof(data->device_control.reg)); + if (error < 0) { + dev_err(&fc->dev, "Failed to write F01 control.\n"); + goto error_exit; + } + + /* dummy read in order to clear irqs */ + error = rmi_read(rmi_dev, fc->fd.data_base_addr + 1, &temp); + if (error < 0) { + dev_err(&fc->dev, "Failed to read Interrupt Status.\n"); + goto error_exit; + } + + error = rmi_read_block(rmi_dev, fc->fd.query_base_addr, + data->basic_queries.regs, + sizeof(data->basic_queries.regs)); + if (error < 0) { + dev_err(&fc->dev, "Failed to read device query registers.\n"); + goto error_exit; + } + driver_data->manufacturer_id = data->basic_queries.manufacturer_id; + + error = rmi_read_block(rmi_dev, + fc->fd.query_base_addr + sizeof(data->basic_queries.regs), + data->product_id, RMI_PRODUCT_ID_LENGTH); + if (error < 0) { + dev_err(&fc->dev, "Failed to read product ID.\n"); + goto error_exit; + } + data->product_id[RMI_PRODUCT_ID_LENGTH] = '\0'; + memcpy(driver_data->product_id, data->product_id, + sizeof(data->product_id)); + + error = rmi_read_block(rmi_dev, fc->fd.data_base_addr, + &data->device_status.reg, + sizeof(data->device_status.reg)); + if (error < 0) { + dev_err(&fc->dev, "Failed to read device status.\n"); + goto error_exit; + } + if (data->device_status.unconfigured) { + dev_err(&fc->dev, + "Device reset during configuration process, status: " + "%#02x!\n", data->device_status.status_code); + error = -EINVAL; + goto error_exit; + } + /* + ** attach the routines that handle sysfs interaction + ** Meaning: Set up sysfs device attributes. + */ + for (attr_count = 0; attr_count < ARRAY_SIZE(fn_01_attrs); + attr_count++) { + if (sysfs_create_file(&fc->dev.kobj, + &fn_01_attrs[attr_count].attr) < 0) { + dev_err(&fc->dev, "Failed to create sysfs file for %s.", + fn_01_attrs[attr_count].attr.name); + error = -ENODEV; + goto error_exit; + } + } + + return error; + + error_exit: + kfree(data); + return error; +} + +#ifdef CONFIG_PM + +static int rmi_f01_suspend(struct rmi_function_container *fc) +{ + struct rmi_device *rmi_dev = fc->rmi_dev; + struct rmi_driver_data *driver_data = rmi_get_driverdata(rmi_dev); + struct f01_data *data = driver_data->f01_container->data; + int retval = 0; + + dev_dbg(&fc->dev, "Suspending...\n"); + if (data->suspended) + return 0; + + data->old_nosleep = data->device_control.nosleep; + data->device_control.nosleep = 0; + data->device_control.sleep_mode = RMI_SLEEP_MODE_SENSOR_SLEEP; + + retval = rmi_write_block(rmi_dev, + driver_data->f01_container->fd.control_base_addr, + &data->device_control.reg, + sizeof(data->device_control.reg)); + if (retval < 0) { + dev_err(&fc->dev, "Failed to write sleep mode. " + "Code: %d.\n", retval); + data->device_control.nosleep = data->old_nosleep; + data->device_control.sleep_mode = RMI_SLEEP_MODE_NORMAL; + } else { + data->suspended = true; + retval = 0; + } + + return retval; +} + +static int rmi_f01_resume(struct rmi_function_container *fc) +{ + struct rmi_device *rmi_dev = fc->rmi_dev; + struct rmi_driver_data *driver_data = rmi_get_driverdata(rmi_dev); + struct f01_data *data = driver_data->f01_container->data; + int retval = 0; + + dev_dbg(&fc->dev, "Resuming...\n"); + if (!data->suspended) + return 0; + + data->device_control.nosleep = data->old_nosleep; + data->device_control.sleep_mode = RMI_SLEEP_MODE_NORMAL; + + retval = rmi_write_block(rmi_dev, + driver_data->f01_container->fd.control_base_addr, + &data->device_control.reg, + sizeof(data->device_control.reg)); + if (retval < 0) + dev_err(&fc->dev, "Failed to restore normal operation. " + "Code: %d.\n", retval); + else { + data->suspended = false; + retval = 0; + } + + return retval; +} +#endif /* CONFIG_PM */ + +static int rmi_f01_init(struct rmi_function_container *fc) +{ + return 0; +} + +static int rmi_f01_attention(struct rmi_function_container *fc, u8 *irq_bits) +{ + struct rmi_device *rmi_dev = fc->rmi_dev; + struct f01_data *data = fc->data; + int error; + + error = rmi_read_block(rmi_dev, fc->fd.data_base_addr, + &data->device_status.reg, + sizeof(data->device_status.reg)); + if (error < 0) { + dev_err(&fc->dev, "Failed to read device status.\n"); + return error; + } + + /* TODO: Do we handle reset here or elsewhere? */ + if (data->device_status.unconfigured) + dev_warn(&rmi_dev->dev, "Reset detected! Status code: %#04x.\n", + data->device_status.status_code); + return 0; +} + +static struct rmi_function_handler function_handler = { + .func = 0x01, + .init = rmi_f01_init, + .attention = rmi_f01_attention, +#ifdef CONFIG_PM + .suspend = rmi_f01_suspend, + .resume = rmi_f01_resume, +#endif +}; + +static int __init rmi_f01_module_init(void) +{ + int error; + + error = rmi_register_function_driver(&function_handler); + if (error < 0) { + pr_err("%s: register failed!\n", __func__); + return error; + } + + return 0; +} + +static void __exit rmi_f01_module_exit(void) +{ + rmi_unregister_function_driver(&function_handler); +} + +module_init(rmi_f01_module_init); +module_exit(rmi_f01_module_exit); + +MODULE_AUTHOR("Christopher Heiny "); +MODULE_DESCRIPTION("RMI F01 module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/rmi4/rmi_f09.c b/drivers/input/touchscreen/rmi4/rmi_f09.c new file mode 100644 index 000000000000..0ec980d7db07 --- /dev/null +++ b/drivers/input/touchscreen/rmi4/rmi_f09.c @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2011 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include + +#define QUERY_BASE_INDEX 1 +#define MAX_LEN 256 + +/* data specific to fn $09 that needs to be kept around */ +struct f09_query { + u8 Limit_Register_Count; + union { + struct { + u8 Result_Register_Count:3; + u8 Reserved:3; + u8 InternalLimits:1; + u8 HostTestEn:1; + }; + u8 f09_bist_query1; + }; +}; + +struct f09_control { + /* test1 */ + u8 Test1LimitLo; + u8 Test1LimitHi; + u8 Test1LimitDiff; + /* test2 */ + u8 Test2LimitLo; + u8 Test2LimitHi; + u8 Test2LimitDiff; +}; + +struct f09_data { + u8 TestNumberControl; + u8 Overall_BIST_Result; + u8 TestResult1; + u8 TestResult2; + u8 Transmitter_Number; + + union { + struct { + u8 Receiver_Number:6; + u8 Limit_Failure_Code:2; + }; + u8 f09_bist_data2; + }; +}; + +struct f09_cmd { + union { + struct { + u8 RunBIST:1; + }; + u8 f09_bist_cmd0; + }; +}; + +struct rmi_fn_09_data { + struct f09_query query; +}; + +static ssize_t rmi_f09_Limit_Register_Count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_f09_HostTestEn_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_f09_HostTestEn_store(struct device *dev, + struct device_attribute *attr, + char *buf, size_t count); + +static ssize_t rmi_f09_InternalLimits_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_f09_Result_Register_Count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_f09_Overall_BIST_Result_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_f09_Overall_BIST_Result_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static struct device_attribute attrs[] = { + __ATTR(Limit_Register_Count, RMI_RO_ATTR, + rmi_f09_Limit_Register_Count_show, rmi_store_error), + __ATTR(HostTestEn, RMI_RW_ATTR, + rmi_f09_HostTestEn_show, rmi_f09_HostTestEn_store), + __ATTR(InternalLimits, RMI_RO_ATTR, + rmi_f09_Limit_Register_Count_show, rmi_store_error), + __ATTR(Result_Register_Count, RMI_RO_ATTR, + rmi_f09_Result_Register_Count_show, rmi_store_error), +}; + +static int rmi_f09_init(struct rmi_function_container *fc) +{ + struct rmi_device *rmi_dev = fc->rmi_dev; + struct rmi_device_platform_data *pdata; + struct rmi_fn_09_data *f09; + u8 query_base_addr; + int rc; + int i; + int attr_count = 0; + int retval = 0; + + dev_info(&fc->dev, "Intializing F09 values."); + + f09 = kzalloc(sizeof(struct rmi_fn_09_data), GFP_KERNEL); + if (!f09) { + dev_err(&fc->dev, "Failed to allocate rmi_fn_09_data.\n"); + retval = -ENOMEM; + goto error_exit; + } + fc->data = f09; + + pdata = to_rmi_platform_data(rmi_dev); + query_base_addr = fc->fd.query_base_addr; + + /* initial all default values for f09 query here */ + rc = rmi_read_block(rmi_dev, query_base_addr, + (u8 *)&f09->query, sizeof(f09->query)); + if (rc < 0) { + dev_err(&fc->dev, "Failed to read query register." + " from 0x%04x\n", query_base_addr); + goto error_exit; + } + + dev_dbg(&fc->dev, "Creating sysfs files."); + /* Set up sysfs device attributes. */ + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + if (sysfs_create_file + (&fc->dev.kobj, &attrs[attr_count].attr) < 0) { + dev_err(&fc->dev, "Failed to create sysfs file for %s.", + attrs[attr_count].attr.name); + retval = -ENODEV; + goto error_exit; + } + } + return 0; + +error_exit: + dev_err(&fc->dev, "An error occured in F09 init!\n"); + for (attr_count--; attr_count >= 0; attr_count--) + sysfs_remove_file(&fc->dev.kobj, + &attrs[attr_count].attr); + kfree(f09); + return retval; +} + +static void rmi_f09_remove(struct rmi_function_container *fc) +{ + struct rmi_fn_09_data *data = fc->data; + if (data) { + kfree(data->query.Limit_Register_Count); + kfree(data->query.f09_bist_query1); + } + kfree(fc->data); +} + +static struct rmi_function_handler function_handler = { + .func = 0x09, + .init = rmi_f09_init, + .remove = rmi_f09_remove +}; + +static int __init rmi_f09_module_init(void) +{ + int error; + + error = rmi_register_function_driver(&function_handler); + if (error < 0) { + pr_err("%s: register failed!\n", __func__); + return error; + } + + return 0; +} + +static void rmi_f09_module_exit(void) +{ + rmi_unregister_function_driver(&function_handler); +} + + +static ssize_t rmi_f09_Limit_Register_Count_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct rmi_fn_09_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.Limit_Register_Count); +} + +static ssize_t rmi_f09_HostTestEn_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct rmi_fn_09_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.HostTestEn); +} + +static ssize_t rmi_f09_HostTestEn_store(struct device *dev, + struct device_attribute *attr, + char *buf, size_t count) +{ + struct rmi_function_container *fc; + struct rmi_fn_09_data *data; + unsigned int new_value; + int result; + + fc = to_rmi_function_container(dev); + data = fc->data; + if (sscanf(buf, "%u", &new_value) != 1) { + dev_err(dev, + "%s: Error - HostTestEn_store has an " + "invalid len.\n", + __func__); + return -EINVAL; + } + + if (new_value < 0 || new_value > 1) { + dev_err(dev, "%s: Invalid HostTestEn bit %s.", __func__, buf); + return -EINVAL; + } + data->query.HostTestEn = new_value; + result = rmi_write(fc->rmi_dev, fc->fd.query_base_addr, + data->query.HostTestEn); + if (result < 0) { + dev_err(dev, "%s : Could not write HostTestEn_store to 0x%x\n", + __func__, fc->fd.query_base_addr); + return result; + } + + return count; + +} + +static ssize_t rmi_f09_InternalLimits_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct rmi_fn_09_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.InternalLimits); +} + +static ssize_t rmi_f09_Result_Register_Count_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct rmi_fn_09_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.Result_Register_Count); +} + +module_init(rmi_f09_module_init); +module_exit(rmi_f09_module_exit); + +MODULE_AUTHOR("Allie Xiong "); +MODULE_DESCRIPTION("RMI F09 module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/rmi4/rmi_f11.c b/drivers/input/touchscreen/rmi4/rmi_f11.c new file mode 100644 index 000000000000..35bb945143de --- /dev/null +++ b/drivers/input/touchscreen/rmi4/rmi_f11.c @@ -0,0 +1,1513 @@ +/* + * Copyright (c) 2011 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +#define F11_MAX_NUM_OF_SENSORS 8 +#define F11_MAX_NUM_OF_FINGERS 10 +#define F11_MAX_NUM_OF_TOUCH_SHAPES 16 + +#define F11_REL_POS_MIN -128 +#define F11_REL_POS_MAX 127 + +#define F11_FINGER_STATE_MASK 0x03 +#define F11_FINGER_STATE_SIZE 0x02 +#define F11_FINGER_STATE_MASK_N(i) \ + (F11_FINGER_STATE_MASK << (i%4 * F11_FINGER_STATE_SIZE)) + +#define F11_FINGER_STATE_VAL_N(f_state, i) \ + (f_state >> (i%4 * F11_FINGER_STATE_SIZE)) + +#define F11_CTRL_SENSOR_MAX_X_POS_OFFSET 6 +#define F11_CTRL_SENSOR_MAX_Y_POS_OFFSET 8 + +#define F11_CEIL(x, y) (((x) + ((y)-1)) / (y)) + +/* By default, we'll support two fingers if we can't figure out how many we + * really need to handle. + */ +#define DEFAULT_NR_OF_FINGERS 2 +#define DEFAULT_XY_MAX 9999 +#define DEFAULT_MAX_ABS_MT_PRESSURE 255 +#define DEFAULT_MAX_ABS_MT_TOUCH 15 +#define DEFAULT_MAX_ABS_MT_ORIENTATION 1 +#define DEFAULT_MIN_ABS_MT_TRACKING_ID 1 +#define DEFAULT_MAX_ABS_MT_TRACKING_ID 10 +#define MAX_LEN 256 + +static ssize_t rmi_fn_11_flip_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_11_flip_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static ssize_t rmi_fn_11_clip_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_11_clip_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static ssize_t rmi_fn_11_offset_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_11_offset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static ssize_t rmi_fn_11_swap_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_11_swap_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static ssize_t rmi_fn_11_relreport_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_fn_11_relreport_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static ssize_t rmi_fn_11_maxPos_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_f11_rezero_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + + +static struct device_attribute attrs[] = { + __ATTR(flip, RMI_RW_ATTR, rmi_fn_11_flip_show, rmi_fn_11_flip_store), + __ATTR(clip, RMI_RW_ATTR, rmi_fn_11_clip_show, rmi_fn_11_clip_store), + __ATTR(offset, RMI_RW_ATTR, + rmi_fn_11_offset_show, rmi_fn_11_offset_store), + __ATTR(swap, RMI_RW_ATTR, rmi_fn_11_swap_show, rmi_fn_11_swap_store), + __ATTR(relreport, RMI_RW_ATTR, + rmi_fn_11_relreport_show, rmi_fn_11_relreport_store), + __ATTR(maxPos, RMI_RO_ATTR, rmi_fn_11_maxPos_show, rmi_store_error), + __ATTR(rezero, RMI_WO_ATTR, rmi_show_error, rmi_f11_rezero_store) +}; + + +union f11_2d_commands { + struct { + u8 rezero:1; + }; + u8 reg; +}; + + +struct f11_2d_device_query { + union { + struct { + u8 nbr_of_sensors:3; + u8 has_query9:1; + u8 has_query11:1; + }; + u8 f11_2d_query0; + }; + + u8 f11_2d_query9; + + union { + struct { + u8 has_z_tuning:1; + u8 has_pos_interpolation_tuning:1; + u8 has_w_tuning:1; + u8 has_pitch_info:1; + u8 has_default_finger_width:1; + u8 has_segmentation_aggressiveness:1; + u8 has_tx_rw_clip:1; + u8 has_drumming_correction:1; + }; + u8 f11_2d_query11; + }; +}; + +struct f11_2d_sensor_query { + union { + struct { + /* query1 */ + u8 number_of_fingers:3; + u8 has_rel:1; + u8 has_abs:1; + u8 has_gestures:1; + u8 has_sensitivity_adjust:1; + u8 configurable:1; + /* query2 */ + u8 num_of_x_electrodes:7; + /* query3 */ + u8 num_of_y_electrodes:7; + /* query4 */ + u8 max_electrodes:7; + }; + u8 f11_2d_query1__4[4]; + }; + + union { + struct { + u8 abs_data_size:3; + u8 has_anchored_finger:1; + u8 has_adj_hyst:1; + u8 has_dribble:1; + }; + u8 f11_2d_query5; + }; + + u8 f11_2d_query6; + + union { + struct { + u8 has_single_tap:1; + u8 has_tap_n_hold:1; + u8 has_double_tap:1; + u8 has_early_tap:1; + u8 has_flick:1; + u8 has_press:1; + u8 has_pinch:1; + u8 padding:1; + + u8 has_palm_det:1; + u8 has_rotate:1; + u8 has_touch_shapes:1; + u8 has_scroll_zones:1; + u8 has_individual_scroll_zones:1; + u8 has_multi_finger_scroll:1; + }; + u8 f11_2d_query7__8[2]; + }; + + /* Empty */ + u8 f11_2d_query9; + + union { + struct { + u8 nbr_touch_shapes:5; + }; + u8 f11_2d_query10; + }; +}; + +struct f11_2d_data_0 { + u8 finger_n; +}; + +struct f11_2d_data_1_5 { + u8 x_msb; + u8 y_msb; + u8 x_lsb:4; + u8 y_lsb:4; + u8 w_y:4; + u8 w_x:4; + u8 z; +}; + +struct f11_2d_data_6_7 { + s8 delta_x; + s8 delta_y; +}; + +struct f11_2d_data_8 { + u8 single_tap:1; + u8 tap_and_hold:1; + u8 double_tap:1; + u8 early_tap:1; + u8 flick:1; + u8 press:1; + u8 pinch:1; +}; + +struct f11_2d_data_9 { + u8 palm_detect:1; + u8 rotate:1; + u8 shape:1; + u8 scrollzone:1; + u8 finger_count:3; +}; + +struct f11_2d_data_10 { + u8 pinch_motion; +}; + +struct f11_2d_data_10_12 { + u8 x_flick_dist; + u8 y_flick_dist; + u8 flick_time; +}; + +struct f11_2d_data_11_12 { + u8 motion; + u8 finger_separation; +}; + +struct f11_2d_data_13 { + u8 shape_n; +}; + +struct f11_2d_data_14_15 { + u8 horizontal; + u8 vertical; +}; + +struct f11_2d_data_14_17 { + u8 x_low; + u8 y_right; + u8 x_upper; + u8 y_left; +}; + +struct f11_2d_data { + const struct f11_2d_data_0 *f_state; + const struct f11_2d_data_1_5 *abs_pos; + const struct f11_2d_data_6_7 *rel_pos; + const struct f11_2d_data_8 *gest_1; + const struct f11_2d_data_9 *gest_2; + const struct f11_2d_data_10 *pinch; + const struct f11_2d_data_10_12 *flick; + const struct f11_2d_data_11_12 *rotate; + const struct f11_2d_data_13 *shapes; + const struct f11_2d_data_14_15 *multi_scroll; + const struct f11_2d_data_14_17 *scroll_zones; +}; + +struct f11_2d_sensor { + struct rmi_f11_2d_axis_alignment axis_align; + struct f11_2d_sensor_query sens_query; + struct f11_2d_data data; + u16 max_x; + u16 max_y; + u8 nbr_fingers; + u8 finger_tracker[F11_MAX_NUM_OF_FINGERS]; + u8 *data_pkt; + int pkt_size; + u8 sensor_index; + char input_name[MAX_LEN]; + char input_phys[MAX_LEN]; + + struct input_dev *input; + struct input_dev *mouse_input; +}; + +struct f11_data { + struct f11_2d_device_query dev_query; + struct rmi_f11_2d_ctrl dev_controls; + struct f11_2d_sensor sensors[F11_MAX_NUM_OF_SENSORS]; +}; + +enum finger_state_values { + F11_NO_FINGER = 0x00, + F11_PRESENT = 0x01, + F11_INACCURATE = 0x02, + F11_RESERVED = 0x03 +}; + +static void rmi_f11_rel_pos_report(struct f11_2d_sensor *sensor, u8 n_finger) +{ + struct f11_2d_data *data = &sensor->data; + struct rmi_f11_2d_axis_alignment *axis_align = &sensor->axis_align; + s8 x, y; + s8 temp; + + x = data->rel_pos[n_finger].delta_x; + y = data->rel_pos[n_finger].delta_y; + + x = min(F11_REL_POS_MAX, max(F11_REL_POS_MIN, (int)x)); + y = min(F11_REL_POS_MAX, max(F11_REL_POS_MIN, (int)y)); + + if (axis_align->swap_axes) { + temp = x; + x = y; + y = temp; + } + if (axis_align->flip_x) + x = min(F11_REL_POS_MAX, -x); + if (axis_align->flip_y) + y = min(F11_REL_POS_MAX, -y); + + if (x || y) { + input_report_rel(sensor->input, REL_X, x); + input_report_rel(sensor->input, REL_Y, y); + input_report_rel(sensor->mouse_input, REL_X, x); + input_report_rel(sensor->mouse_input, REL_Y, y); + } + input_sync(sensor->mouse_input); +} + +static void rmi_f11_abs_pos_report(struct f11_2d_sensor *sensor, + u8 finger_state, u8 n_finger) +{ + struct f11_2d_data *data = &sensor->data; + struct rmi_f11_2d_axis_alignment *axis_align = &sensor->axis_align; + int prev_state = sensor->finger_tracker[n_finger]; + int x, y, z; + int w_x, w_y, w_max, w_min, orient; + int temp; + if (prev_state && !finger_state) { + /* this is a release */ + x = y = z = w_max = w_min = orient = 0; + } else if (!prev_state && !finger_state) { + /* nothing to report */ + return; + } else { + x = ((data->abs_pos[n_finger].x_msb << 4) | + data->abs_pos[n_finger].x_lsb); + y = ((data->abs_pos[n_finger].y_msb << 4) | + data->abs_pos[n_finger].y_lsb); + z = data->abs_pos[n_finger].z; + w_x = data->abs_pos[n_finger].w_x; + w_y = data->abs_pos[n_finger].w_y; + w_max = max(w_x, w_y); + w_min = min(w_x, w_y); + + if (axis_align->swap_axes) { + temp = x; + x = y; + y = temp; + temp = w_x; + w_x = w_y; + w_y = temp; + } + + orient = w_x > w_y ? 1 : 0; + + if (axis_align->flip_x) + x = max(sensor->max_x - x, 0); + + if (axis_align->flip_y) + y = max(sensor->max_y - y, 0); + + /* + ** here checking if X offset or y offset are specified is + ** redundant. We just add the offsets or, clip the values + ** + ** note: offsets need to be done before clipping occurs, + ** or we could get funny values that are outside + ** clipping boundaries. + */ + x += axis_align->offset_X; + y += axis_align->offset_Y; + x = max(axis_align->clip_X_low, x); + y = max(axis_align->clip_Y_low, y); + if (axis_align->clip_X_high) + x = min(axis_align->clip_X_high, x); + if (axis_align->clip_Y_high) + y = min(axis_align->clip_Y_high, y); + + } + + pr_debug("%s: f_state[%d]:%d - x:%d y:%d z:%d w_max:%d w_min:%d\n", + __func__, n_finger, finger_state, x, y, z, w_max, w_min); + + +#ifdef ABS_MT_PRESSURE + input_report_abs(sensor->input, ABS_MT_PRESSURE, z); +#endif + input_report_abs(sensor->input, ABS_MT_TOUCH_MAJOR, w_max); + input_report_abs(sensor->input, ABS_MT_TOUCH_MINOR, w_min); + input_report_abs(sensor->input, ABS_MT_ORIENTATION, orient); + input_report_abs(sensor->input, ABS_MT_POSITION_X, x); + input_report_abs(sensor->input, ABS_MT_POSITION_Y, y); + input_report_abs(sensor->input, ABS_MT_TRACKING_ID, n_finger); + + /* MT sync between fingers */ + input_mt_sync(sensor->input); + sensor->finger_tracker[n_finger] = finger_state; +} + +static void rmi_f11_finger_handler(struct f11_2d_sensor *sensor) +{ + const struct f11_2d_data_0 *f_state = sensor->data.f_state; + u8 finger_state; + u8 finger_pressed_count; + u8 i; + + for (i = 0, finger_pressed_count = 0; i < sensor->nbr_fingers; i++) { + /* Possible of having 4 fingers per f_statet register */ + finger_state = (f_state[i >> 2].finger_n & + F11_FINGER_STATE_MASK_N(i)); + finger_state = F11_FINGER_STATE_VAL_N(finger_state, i); + + if (finger_state == F11_RESERVED) { + pr_err("%s: Invalid finger state[%d]:0x%02x.", __func__, + i, finger_state); + continue; + } else if ((finger_state == F11_PRESENT) || + (finger_state == F11_INACCURATE)) { + finger_pressed_count++; + } + + if (sensor->data.abs_pos) + rmi_f11_abs_pos_report(sensor, finger_state, i); + + if (sensor->data.rel_pos) + rmi_f11_rel_pos_report(sensor, i); + } + input_report_key(sensor->input, BTN_TOUCH, finger_pressed_count); + input_sync(sensor->input); +} + +static inline int rmi_f11_2d_construct_data(struct f11_2d_sensor *sensor) +{ + struct f11_2d_sensor_query *query = &sensor->sens_query; + struct f11_2d_data *data = &sensor->data; + int i; + + sensor->nbr_fingers = (query->number_of_fingers == 5 ? 10 : + query->number_of_fingers + 1); + + sensor->pkt_size = F11_CEIL(sensor->nbr_fingers, 4); + + if (query->has_abs) + sensor->pkt_size += (sensor->nbr_fingers * 5); + + if (query->has_rel) + sensor->pkt_size += (sensor->nbr_fingers * 2); + + /* Check if F11_2D_Query7 is non-zero */ + if (query->f11_2d_query7__8[0]) + sensor->pkt_size += sizeof(u8); + + /* Check if F11_2D_Query7 or F11_2D_Query8 is non-zero */ + if (query->f11_2d_query7__8[0] || query->f11_2d_query7__8[1]) + sensor->pkt_size += sizeof(u8); + + if (query->has_pinch || query->has_flick || query->has_rotate) { + sensor->pkt_size += 3; + if (!query->has_flick) + sensor->pkt_size--; + if (!query->has_rotate) + sensor->pkt_size--; + } + + if (query->has_touch_shapes) + sensor->pkt_size += F11_CEIL(query->nbr_touch_shapes + 1, 8); + + sensor->data_pkt = kzalloc(sensor->pkt_size, GFP_KERNEL); + if (!sensor->data_pkt) + return -ENOMEM; + + data->f_state = (struct f11_2d_data_0 *)sensor->data_pkt; + i = F11_CEIL(sensor->nbr_fingers, 4); + + if (query->has_abs) { + data->abs_pos = (struct f11_2d_data_1_5 *) + &sensor->data_pkt[i]; + i += (sensor->nbr_fingers * 5); + } + + if (query->has_rel) { + data->rel_pos = (struct f11_2d_data_6_7 *) + &sensor->data_pkt[i]; + i += (sensor->nbr_fingers * 2); + } + + if (query->f11_2d_query7__8[0]) { + data->gest_1 = (struct f11_2d_data_8 *)&sensor->data_pkt[i]; + i++; + } + + if (query->f11_2d_query7__8[0] || query->f11_2d_query7__8[1]) { + data->gest_2 = (struct f11_2d_data_9 *)&sensor->data_pkt[i]; + i++; + } + + if (query->has_pinch) { + data->pinch = (struct f11_2d_data_10 *)&sensor->data_pkt[i]; + i++; + } + + if (query->has_flick) { + if (query->has_pinch) { + data->flick = (struct f11_2d_data_10_12 *)data->pinch; + i += 2; + } else { + data->flick = (struct f11_2d_data_10_12 *) + &sensor->data_pkt[i]; + i += 3; + } + } + + if (query->has_rotate) { + if (query->has_flick) { + data->rotate = (struct f11_2d_data_11_12 *) + (data->flick + 1); + } else { + data->rotate = (struct f11_2d_data_11_12 *) + &sensor->data_pkt[i]; + i += 2; + } + } + + if (query->has_touch_shapes) + data->shapes = (struct f11_2d_data_13 *)&sensor->data_pkt[i]; + + return 0; +} + +static int rmi_f11_read_control_parameters(struct rmi_device *rmi_dev, + struct f11_2d_device_query *query, + struct rmi_f11_2d_ctrl *ctrl, + int ctrl_base_addr) { + int read_address = ctrl_base_addr; + int error = 0; + + if (ctrl->ctrl0) { + error = rmi_read_block(rmi_dev, read_address, &ctrl->ctrl0->reg, + sizeof(union rmi_f11_2d_ctrl0)); + if (error < 0) { + dev_err(&rmi_dev->dev, + "Failed to read F11 ctrl0, code: %d.\n", error); + return error; + } + read_address = read_address + sizeof(union rmi_f11_2d_ctrl0); + } + + if (ctrl->ctrl1) { + error = rmi_read_block(rmi_dev, read_address, &ctrl->ctrl1->reg, + sizeof(union rmi_f11_2d_ctrl1)); + if (error < 0) { + dev_err(&rmi_dev->dev, + "Failed to read F11 ctrl1, code: %d.\n", error); + return error; + } + read_address = read_address + sizeof(union rmi_f11_2d_ctrl1); + } + + if (ctrl->ctrl2__3) { + error = rmi_read_block(rmi_dev, read_address, + ctrl->ctrl2__3->regs, + sizeof(ctrl->ctrl2__3->regs)); + if (error < 0) { + dev_err(&rmi_dev->dev, + "Failed to read F11 ctrl2__3, code: %d.\n", + error); + return error; + } + read_address = read_address + sizeof(ctrl->ctrl2__3->regs); + } + + if (ctrl->ctrl4) { + error = rmi_read_block(rmi_dev, read_address, &ctrl->ctrl4->reg, + sizeof(ctrl->ctrl4->reg)); + if (error < 0) { + dev_err(&rmi_dev->dev, + "Failed to read F11 ctrl4, code: %d.\n", error); + return error; + } + read_address = read_address + sizeof(ctrl->ctrl4->reg); + } + + if (ctrl->ctrl5) { + error = rmi_read_block(rmi_dev, read_address, &ctrl->ctrl5->reg, + sizeof(ctrl->ctrl5->reg)); + if (error < 0) { + dev_err(&rmi_dev->dev, + "Failed to read F11 ctrl5, code: %d.\n", error); + return error; + } + read_address = read_address + sizeof(ctrl->ctrl5->reg); + } + + if (ctrl->ctrl6__7) { + error = rmi_read_block(rmi_dev, read_address, + ctrl->ctrl6__7->regs, + sizeof(ctrl->ctrl6__7->regs)); + if (error < 0) { + dev_err(&rmi_dev->dev, + "Failed to read F11 ctrl6__7, code: %d.\n", + error); + return error; + } + read_address = read_address + sizeof(ctrl->ctrl6__7->regs); + } + + if (ctrl->ctrl8__9) { + error = rmi_read_block(rmi_dev, read_address, + ctrl->ctrl8__9->regs, + sizeof(ctrl->ctrl8__9->regs)); + if (error < 0) { + dev_err(&rmi_dev->dev, + "Failed to read F11 ctrl8__9, code: %d.\n", + error); + return error; + } + read_address = read_address + sizeof(ctrl->ctrl8__9->regs); + } + + return 0; +} + +static int rmi_f11_initialize_control_parameters(struct rmi_device *rmi_dev, + struct f11_2d_device_query *query, + struct rmi_f11_2d_ctrl *ctrl, + int ctrl_base_addr) { + int error = 0; + + ctrl->ctrl0 = kzalloc(sizeof(union rmi_f11_2d_ctrl0), GFP_KERNEL); + if (!ctrl->ctrl0) { + dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl0.\n"); + error = -ENOMEM; + goto error_exit; + } + + ctrl->ctrl1 = kzalloc(sizeof(union rmi_f11_2d_ctrl1), GFP_KERNEL); + if (!ctrl->ctrl1) { + dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl1.\n"); + error = -ENOMEM; + goto error_exit; + } + + ctrl->ctrl2__3 = kzalloc(sizeof(union rmi_f11_2d_ctrl2__3), GFP_KERNEL); + if (!ctrl->ctrl2__3) { + dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl2__3.\n"); + error = -ENOMEM; + goto error_exit; + } + + ctrl->ctrl4 = kzalloc(sizeof(union rmi_f11_2d_ctrl4), GFP_KERNEL); + if (!ctrl->ctrl4) { + dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl4.\n"); + error = -ENOMEM; + goto error_exit; + } + + ctrl->ctrl5 = kzalloc(sizeof(union rmi_f11_2d_ctrl5), GFP_KERNEL); + if (!ctrl->ctrl5) { + dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl5.\n"); + error = -ENOMEM; + goto error_exit; + } + + ctrl->ctrl6__7 = kzalloc(sizeof(union rmi_f11_2d_ctrl6__7), GFP_KERNEL); + if (!ctrl->ctrl6__7) { + dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl6__7.\n"); + error = -ENOMEM; + goto error_exit; + } + + ctrl->ctrl8__9 = kzalloc(sizeof(union rmi_f11_2d_ctrl8__9), GFP_KERNEL); + if (!ctrl->ctrl8__9) { + dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl8__9.\n"); + error = -ENOMEM; + goto error_exit; + } + + return rmi_f11_read_control_parameters(rmi_dev, query, + ctrl, ctrl_base_addr); + +error_exit: + kfree(ctrl->ctrl0); + kfree(ctrl->ctrl1); + kfree(ctrl->ctrl2__3); + kfree(ctrl->ctrl4); + kfree(ctrl->ctrl5); + kfree(ctrl->ctrl6__7); + kfree(ctrl->ctrl8__9); + + return error; +} + +static inline int rmi_f11_set_control_parameters(struct rmi_device *rmi_dev, + struct f11_2d_sensor_query *query, + struct rmi_f11_2d_ctrl *ctrl, + int ctrl_base_addr) +{ + int write_address = ctrl_base_addr; + int error; + + if (ctrl->ctrl0) { + error = rmi_write_block(rmi_dev, write_address, + &ctrl->ctrl0->reg, + 1); + if (error < 0) + return error; + write_address++; + } + + if (ctrl->ctrl1) { + error = rmi_write_block(rmi_dev, write_address, + &ctrl->ctrl1->reg, + 1); + if (error < 0) + return error; + write_address++; + } + + if (ctrl->ctrl2__3) { + error = rmi_write_block(rmi_dev, write_address, + ctrl->ctrl2__3->regs, + sizeof(ctrl->ctrl2__3->regs)); + if (error < 0) + return error; + write_address += sizeof(ctrl->ctrl2__3->regs); + } + + if (ctrl->ctrl4) { + error = rmi_write_block(rmi_dev, write_address, + &ctrl->ctrl4->reg, + 1); + if (error < 0) + return error; + write_address++; + } + + if (ctrl->ctrl5) { + error = rmi_write_block(rmi_dev, write_address, + &ctrl->ctrl5->reg, + 1); + if (error < 0) + return error; + write_address++; + } + + if (ctrl->ctrl6__7) { + error = rmi_write_block(rmi_dev, write_address, + &ctrl->ctrl6__7->regs[0], + sizeof(ctrl->ctrl6__7->regs)); + if (error < 0) + return error; + write_address += sizeof(ctrl->ctrl6__7->regs); + } + + if (ctrl->ctrl8__9) { + error = rmi_write_block(rmi_dev, write_address, + &ctrl->ctrl8__9->regs[0], + sizeof(ctrl->ctrl8__9->regs)); + if (error < 0) + return error; + write_address += sizeof(ctrl->ctrl8__9->regs); + } + + if (ctrl->ctrl10) { + error = rmi_write_block(rmi_dev, write_address, + &ctrl->ctrl10->reg, + 1); + if (error < 0) + return error; + write_address++; + } + + if (ctrl->ctrl11) { + error = rmi_write_block(rmi_dev, write_address, + &ctrl->ctrl11->reg, + 1); + if (error < 0) + return error; + write_address++; + } + + if (ctrl->ctrl12 && ctrl->ctrl12_size && query->configurable) { + if (ctrl->ctrl12_size > query->max_electrodes) { + dev_err(&rmi_dev->dev, + "%s: invalid cfg size:%d, should be < %d.\n", + __func__, ctrl->ctrl12_size, + query->max_electrodes); + return -EINVAL; + } + error = rmi_write_block(rmi_dev, write_address, + &ctrl->ctrl12->reg, + ctrl->ctrl12_size); + if (error < 0) + return error; + write_address += ctrl->ctrl12_size; + } + + if (ctrl->ctrl14) { + error = rmi_write_block(rmi_dev, + write_address, + &ctrl->ctrl0->reg, + 1); + if (error < 0) + return error; + write_address++; + } + + if (ctrl->ctrl15) { + error = rmi_write_block(rmi_dev, write_address, + ctrl->ctrl15, + 1); + if (error < 0) + return error; + write_address++; + } + + if (ctrl->ctrl16) { + error = rmi_write_block(rmi_dev, write_address, + ctrl->ctrl16, + 1); + if (error < 0) + return error; + write_address++; + } + + if (ctrl->ctrl17) { + error = rmi_write_block(rmi_dev, write_address, + ctrl->ctrl17, + 1); + if (error < 0) + return error; + write_address++; + } + + if (ctrl->ctrl18) { + error = rmi_write_block(rmi_dev, write_address, + ctrl->ctrl18, + 1); + if (error < 0) + return error; + write_address++; + } + + if (ctrl->ctrl19) { + error = rmi_write_block(rmi_dev, write_address, + ctrl->ctrl19, + 1); + if (error < 0) + return error; + write_address++; + } + + return 0; +} + +static inline int rmi_f11_get_query_parameters(struct rmi_device *rmi_dev, + struct f11_2d_sensor_query *query, u8 query_base_addr) +{ + int query_size; + int rc; + + rc = rmi_read_block(rmi_dev, query_base_addr, query->f11_2d_query1__4, + sizeof(query->f11_2d_query1__4)); + if (rc < 0) + return rc; + query_size = rc; + + if (query->has_abs) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, + &query->f11_2d_query5); + if (rc < 0) + return rc; + query_size++; + } + + if (query->has_rel) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, + &query->f11_2d_query6); + if (rc < 0) + return rc; + query_size++; + } + + if (query->has_gestures) { + rc = rmi_read_block(rmi_dev, query_base_addr + query_size, + query->f11_2d_query7__8, + sizeof(query->f11_2d_query7__8)); + if (rc < 0) + return rc; + query_size += sizeof(query->f11_2d_query7__8); + } + + if (query->has_touch_shapes) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, + &query->f11_2d_query10); + if (rc < 0) + return rc; + query_size++; + } + + return query_size; +} + +/* This operation is done in a number of places, so we have a handy routine + * for it. + */ +static void f11_set_abs_params(struct rmi_function_container *fc, int index) +{ + struct f11_data *instance_data = fc->data; + struct input_dev *input = instance_data->sensors[index].input; + int device_x_max = + instance_data->dev_controls.ctrl6__7->sensor_max_x_pos; + int device_y_max = + instance_data->dev_controls.ctrl8__9->sensor_max_y_pos; + int x_min, x_max, y_min, y_max; + + if (instance_data->sensors[index].axis_align.swap_axes) { + int temp = device_x_max; + device_x_max = device_y_max; + device_y_max = temp; + } + + /* Use the max X and max Y read from the device, or the clip values, + * whichever is stricter. + */ + x_min = instance_data->sensors[index].axis_align.clip_X_low; + if (instance_data->sensors[index].axis_align.clip_X_high) + x_max = min((int) device_x_max, + instance_data->sensors[index].axis_align.clip_X_high); + else + x_max = device_x_max; + + y_min = instance_data->sensors[index].axis_align.clip_Y_low; + if (instance_data->sensors[index].axis_align.clip_Y_high) + y_max = min((int) device_y_max, + instance_data->sensors[index].axis_align.clip_Y_high); + else + y_max = device_y_max; + + dev_dbg(&fc->dev, "Set ranges X=[%d..%d] Y=[%d..%d].", + x_min, x_max, y_min, y_max); + +#ifdef ABS_MT_PRESSURE + input_set_abs_params(input, ABS_MT_PRESSURE, 0, + DEFAULT_MAX_ABS_MT_PRESSURE, 0, 0); +#endif + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, + 0, DEFAULT_MAX_ABS_MT_TOUCH, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MINOR, + 0, DEFAULT_MAX_ABS_MT_TOUCH, 0, 0); + input_set_abs_params(input, ABS_MT_ORIENTATION, + 0, DEFAULT_MAX_ABS_MT_ORIENTATION, 0, 0); + input_set_abs_params(input, ABS_MT_TRACKING_ID, + DEFAULT_MIN_ABS_MT_TRACKING_ID, + DEFAULT_MAX_ABS_MT_TRACKING_ID, 0, 0); + /* TODO get max_x_pos (and y) from control registers. */ + input_set_abs_params(input, ABS_MT_POSITION_X, + x_min, x_max, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, + y_min, y_max, 0, 0); +} + +static int rmi_f11_init(struct rmi_function_container *fc) +{ + struct rmi_device *rmi_dev = fc->rmi_dev; + struct rmi_device_platform_data *pdata; + struct f11_data *f11; + struct input_dev *input_dev; + struct input_dev *input_dev_mouse; + u8 query_offset; + u8 query_base_addr; + u8 control_base_addr; + u16 max_x_pos, max_y_pos, temp; + int rc; + int i; + int retval = 0; + int attr_count = 0; + + dev_info(&fc->dev, "Intializing F11 values."); + + /* + ** init instance data, fill in values and create any sysfs files + */ + f11 = kzalloc(sizeof(struct f11_data), GFP_KERNEL); + if (!f11) + return -ENOMEM; + fc->data = f11; + + query_base_addr = fc->fd.query_base_addr; + control_base_addr = fc->fd.control_base_addr; + + rc = rmi_read(rmi_dev, query_base_addr, &f11->dev_query.f11_2d_query0); + if (rc < 0) + goto err_free_data; + + rc = rmi_f11_initialize_control_parameters(rmi_dev, &f11->dev_query, + &f11->dev_controls, control_base_addr); + if (rc < 0) { + dev_err(&fc->dev, + "Failed to initialize F11 control params.\n"); + goto err_free_data; + } + + query_offset = (query_base_addr + 1); + /* Increase with one since number of sensors is zero based */ + for (i = 0; i < (f11->dev_query.nbr_of_sensors + 1); i++) { + f11->sensors[i].sensor_index = i; + + rc = rmi_f11_get_query_parameters(rmi_dev, + &f11->sensors[i].sens_query, + query_offset); + if (rc < 0) + goto err_free_data; + + query_offset += rc; + + pdata = to_rmi_platform_data(rmi_dev); + if (pdata) + f11->sensors[i].axis_align = pdata->axis_align; + + if (pdata && pdata->f11_ctrl) { + rc = rmi_f11_set_control_parameters(rmi_dev, + &f11->sensors[i].sens_query, + pdata->f11_ctrl, + control_base_addr); + if (rc < 0) + goto err_free_data; + } + + if (pdata && pdata->f11_ctrl && + pdata->f11_ctrl->ctrl6__7 && + pdata->f11_ctrl->ctrl8__9) { + max_x_pos = pdata->f11_ctrl->ctrl6__7->sensor_max_x_pos; + max_y_pos = pdata->f11_ctrl->ctrl8__9->sensor_max_y_pos; + + } else { + rc = rmi_read_block(rmi_dev, + control_base_addr + F11_CTRL_SENSOR_MAX_X_POS_OFFSET, + (u8 *)&max_x_pos, sizeof(max_x_pos)); + if (rc < 0) + goto err_free_data; + + rc = rmi_read_block(rmi_dev, + control_base_addr + F11_CTRL_SENSOR_MAX_Y_POS_OFFSET, + (u8 *)&max_y_pos, sizeof(max_y_pos)); + if (rc < 0) + goto err_free_data; + } + + if (pdata->axis_align.swap_axes) { + temp = max_x_pos; + max_x_pos = max_y_pos; + max_y_pos = temp; + } + f11->sensors[i].max_x = max_x_pos; + f11->sensors[i].max_y = max_y_pos; + + rc = rmi_f11_2d_construct_data(&f11->sensors[i]); + if (rc < 0) + goto err_free_data; + + input_dev = input_allocate_device(); + if (!input_dev) { + rc = -ENOMEM; + goto err_free_data; + } + + f11->sensors[i].input = input_dev; + /* TODO how to modify the dev name and + * phys name for input device */ + sprintf(f11->sensors[i].input_name, "%sfn%02x", + dev_name(&rmi_dev->dev), fc->fd.function_number); + input_dev->name = f11->sensors[i].input_name; + sprintf(f11->sensors[i].input_phys, "%s/input0", + input_dev->name); + input_dev->phys = f11->sensors[i].input_phys; + input_dev->dev.parent = &rmi_dev->dev; + input_set_drvdata(input_dev, f11); + + set_bit(EV_SYN, input_dev->evbit); + set_bit(EV_KEY, input_dev->evbit); + set_bit(EV_ABS, input_dev->evbit); + + f11_set_abs_params(fc, i); + + dev_dbg(&fc->dev, "%s: Sensor %d hasRel %d.\n", + __func__, i, f11->sensors[i].sens_query.has_rel); + if (f11->sensors[i].sens_query.has_rel) { + set_bit(EV_REL, input_dev->evbit); + set_bit(REL_X, input_dev->relbit); + set_bit(REL_Y, input_dev->relbit); + } + rc = input_register_device(input_dev); + if (rc < 0) + goto err_free_input; + + if (f11->sensors[i].sens_query.has_rel) { + /*create input device for mouse events */ + input_dev_mouse = input_allocate_device(); + if (!input_dev_mouse) { + rc = -ENOMEM; + goto err_free_data; + } + + f11->sensors[i].mouse_input = input_dev_mouse; + input_dev_mouse->name = "rmi_mouse"; + input_dev_mouse->phys = "rmi_f11/input0"; + + input_dev_mouse->id.vendor = 0x18d1; + input_dev_mouse->id.product = 0x0210; + input_dev_mouse->id.version = 0x0100; + + set_bit(EV_REL, input_dev_mouse->evbit); + set_bit(REL_X, input_dev_mouse->relbit); + set_bit(REL_Y, input_dev_mouse->relbit); + + set_bit(BTN_MOUSE, input_dev_mouse->evbit); + /* Register device's buttons and keys */ + set_bit(EV_KEY, input_dev_mouse->evbit); + set_bit(BTN_LEFT, input_dev_mouse->keybit); + set_bit(BTN_MIDDLE, input_dev_mouse->keybit); + set_bit(BTN_RIGHT, input_dev_mouse->keybit); + + rc = input_register_device(input_dev_mouse); + if (rc < 0) + goto err_free_input; + set_bit(BTN_RIGHT, input_dev_mouse->keybit); + } + + } + + dev_info(&fc->dev, "Creating sysfs files."); + dev_dbg(&fc->dev, "Creating fn11 sysfs files."); + + /* Set up sysfs device attributes. */ + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + if (sysfs_create_file + (&fc->dev.kobj, &attrs[attr_count].attr) < 0) { + dev_err(&fc->dev, "Failed to create sysfs file for %s.", + attrs[attr_count].attr.name); + retval = -ENODEV; + goto err_free_input; + } + } + + dev_info(&fc->dev, "Done Creating fn11 sysfs files."); + return 0; + +err_free_input: + for (i = 0; i < (f11->dev_query.nbr_of_sensors + 1); i++) { + if (f11->sensors[i].input) + input_free_device(f11->sensors[i].input); + if (f11->sensors[i].sens_query.has_rel && + f11->sensors[i].mouse_input) + input_free_device(f11->sensors[i].mouse_input); + } +err_free_data: + for (attr_count--; attr_count >= 0; attr_count--) + device_remove_file(&fc->rmi_dev->dev, &attrs[attr_count]); + + kfree(f11); + return rc; +} + +int rmi_f11_attention(struct rmi_function_container *fc, u8 *irq_bits) +{ + struct rmi_device *rmi_dev = fc->rmi_dev; + struct f11_data *f11 = fc->data; + u8 data_base_addr = fc->fd.data_base_addr; + int data_base_addr_offset = 0; + int error; + int i; + + for (i = 0; i < f11->dev_query.nbr_of_sensors + 1; i++) { + error = rmi_read_block(rmi_dev, + data_base_addr + data_base_addr_offset, + f11->sensors[i].data_pkt, + f11->sensors[i].pkt_size); + if (error < 0) + return error; + + rmi_f11_finger_handler(&f11->sensors[i]); + data_base_addr_offset += f11->sensors[i].pkt_size; + } + return 0; +} + +static void rmi_f11_remove(struct rmi_function_container *fc) +{ + struct f11_data *data = fc->data; + int i; + + for (i = 0; i < (data->dev_query.nbr_of_sensors + 1); i++) { + input_unregister_device(data->sensors[i].input); + if (data->sensors[i].sens_query.has_rel) + input_unregister_device(data->sensors[i].mouse_input); + } + kfree(fc->data); +} + +static struct rmi_function_handler function_handler = { + .func = 0x11, + .init = rmi_f11_init, + .attention = rmi_f11_attention, + .remove = rmi_f11_remove +}; + +static int __init rmi_f11_module_init(void) +{ + int error; + + error = rmi_register_function_driver(&function_handler); + if (error < 0) { + pr_err("%s: register failed!\n", __func__); + return error; + } + + return 0; +} + +static void __exit rmi_f11_module_exit(void) +{ + rmi_unregister_function_driver(&function_handler); +} + +static ssize_t rmi_fn_11_maxPos_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct f11_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u %u\n", + data->sensors[0].max_x, data->sensors[0].max_y); +} + +static ssize_t rmi_fn_11_flip_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct f11_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u %u\n", + data->sensors[0].axis_align.flip_x, + data->sensors[0].axis_align.flip_y); +} + +static ssize_t rmi_fn_11_flip_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct rmi_function_container *fc; + struct f11_data *instance_data; + unsigned int new_X, new_Y; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + + if (sscanf(buf, "%u %u", &new_X, &new_Y) != 2) + return -EINVAL; + if (new_X < 0 || new_X > 1 || new_Y < 0 || new_Y > 1) + return -EINVAL; + instance_data->sensors[0].axis_align.flip_x = new_X; + instance_data->sensors[0].axis_align.flip_y = new_Y; + + return count; +} + +static ssize_t rmi_fn_11_swap_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rmi_function_container *fc; + struct f11_data *instance_data; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + instance_data->sensors[0].axis_align.swap_axes); +} + +static ssize_t rmi_fn_11_swap_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rmi_function_container *fc; + struct f11_data *instance_data; + unsigned int newSwap; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + + if (sscanf(buf, "%u", &newSwap) != 1) + return -EINVAL; + if (newSwap < 0 || newSwap > 1) + return -EINVAL; + instance_data->sensors[0].axis_align.swap_axes = newSwap; + + f11_set_abs_params(fc, 0); + + return count; +} + +static ssize_t rmi_fn_11_relreport_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct f11_data *instance_data; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + instance_data-> + sensors[0].axis_align.rel_report_enabled); +} + +static ssize_t rmi_fn_11_relreport_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct rmi_function_container *fc; + struct f11_data *instance_data; + unsigned int new_value; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + + if (sscanf(buf, "%u", &new_value) != 1) + return -EINVAL; + if (new_value < 0 || new_value > 1) + return -EINVAL; + instance_data->sensors[0].axis_align.rel_report_enabled = new_value; + + return count; +} + +static ssize_t rmi_fn_11_offset_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct f11_data *instance_data; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%d %d\n", + instance_data->sensors[0].axis_align.offset_X, + instance_data->sensors[0].axis_align.offset_Y); +} + +static ssize_t rmi_fn_11_offset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct rmi_function_container *fc; + struct f11_data *instance_data; + int new_X, new_Y; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + + if (sscanf(buf, "%d %d", &new_X, &new_Y) != 2) + return -EINVAL; + instance_data->sensors[0].axis_align.offset_X = new_X; + instance_data->sensors[0].axis_align.offset_Y = new_Y; + + return count; +} + +static ssize_t rmi_fn_11_clip_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + + struct rmi_function_container *fc; + struct f11_data *instance_data; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u %u %u %u\n", + instance_data->sensors[0].axis_align.clip_X_low, + instance_data->sensors[0].axis_align.clip_X_high, + instance_data->sensors[0].axis_align.clip_Y_low, + instance_data->sensors[0].axis_align.clip_Y_high); +} + +static ssize_t rmi_fn_11_clip_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct rmi_function_container *fc; + struct f11_data *instance_data; + unsigned int new_X_low, new_X_high, new_Y_low, new_Y_high; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + if (sscanf(buf, "%u %u %u %u", + &new_X_low, &new_X_high, &new_Y_low, &new_Y_high) != 4) + return -EINVAL; + if (new_X_low < 0 || new_X_low >= new_X_high || new_Y_low < 0 + || new_Y_low >= new_Y_high) + return -EINVAL; + instance_data->sensors[0].axis_align.clip_X_low = new_X_low; + instance_data->sensors[0].axis_align.clip_X_high = new_X_high; + instance_data->sensors[0].axis_align.clip_Y_low = new_Y_low; + instance_data->sensors[0].axis_align.clip_Y_high = new_Y_high; + + /* + ** for now, we assume this is sensor index 0 + */ + f11_set_abs_params(fc, 0); + + return count; +} + +static ssize_t rmi_f11_rezero_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rmi_function_container *fc = NULL; + unsigned int rezero; + int retval = 0; + /* Command register always reads as 0, so we can just use a local. */ + union f11_2d_commands commands = {}; + + fc = to_rmi_function_container(dev); + + if (sscanf(buf, "%u", &rezero) != 1) + return -EINVAL; + if (rezero < 0 || rezero > 1) + return -EINVAL; + + /* Per spec, 0 has no effect, so we skip it entirely. */ + if (rezero) { + commands.rezero = 1; + retval = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr, + &commands.reg, sizeof(commands.reg)); + if (retval < 0) { + dev_err(dev, "%s: failed to issue rezero command, " + "error = %d.", __func__, retval); + return retval; + } + } + + return count; +} + + +module_init(rmi_f11_module_init); +module_exit(rmi_f11_module_exit); + +MODULE_AUTHOR("Stefan Nilsson "); +MODULE_DESCRIPTION("RMI F11 module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/rmi4/rmi_f19.c b/drivers/input/touchscreen/rmi4/rmi_f19.c new file mode 100644 index 000000000000..9ff9ff99f91f --- /dev/null +++ b/drivers/input/touchscreen/rmi4/rmi_f19.c @@ -0,0 +1,1419 @@ +/* + * Copyright (c) 2011 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include + +#define QUERY_BASE_INDEX 1 +#define MAX_LEN 256 + +struct f19_0d_query { + union { + struct { + u8 configurable:1; + u8 has_sensitivity_adjust:1; + u8 has_hysteresis_threshold:1; + }; + u8 f19_0d_query0; + }; + u8 f19_0d_query1:5; +}; + +struct f19_0d_control_0 { + union { + struct { + u8 button_usage:2; + u8 filter_mode:2; + }; + u8 f19_0d_control0; + }; +}; + +struct f19_0d_control_1 { + u8 int_enabled_button; +}; + +struct f19_0d_control_2 { + u8 single_button; +}; + +struct f19_0d_control_3_4 { + u8 sensor_map_button:7; + /*u8 sensitivity_button;*/ +}; + +struct f19_0d_control_5 { + u8 sensitivity_adj; +}; +struct f19_0d_control_6 { + u8 hysteresis_threshold; +}; + +struct f19_0d_control { + struct f19_0d_control_0 *general_control; + struct f19_0d_control_1 *button_int_enable; + struct f19_0d_control_2 *single_button_participation; + struct f19_0d_control_3_4 *sensor_map; + struct f19_0d_control_5 *all_button_sensitivity_adj; + struct f19_0d_control_6 *all_button_hysteresis_threshold; +}; +/* data specific to fn $19 that needs to be kept around */ +struct f19_data { + struct f19_0d_control *button_control; + struct f19_0d_query button_query; + u8 button_rezero; + bool *button_down; + unsigned char button_count; + unsigned char button_data_buffer_size; + unsigned char *button_data_buffer; + unsigned char *button_map; + char input_name[MAX_LEN]; + char input_phys[MAX_LEN]; + struct input_dev *input; +}; + +static ssize_t rmi_f19_button_count_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_f19_button_map_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_f19_button_map_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static ssize_t rmi_f19_rezero_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t rmi_f19_rezero_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static ssize_t rmi_f19_has_hysteresis_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t rmi_f19_has_sensitivity_adjust_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t rmi_f19_configurable_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t rmi_f19_filter_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t rmi_f19_filter_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static ssize_t rmi_f19_button_usage_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t rmi_f19_button_usage_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static ssize_t rmi_f19_interrupt_enable_button_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t rmi_f19_interrupt_enable_button_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static ssize_t rmi_f19_single_button_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t rmi_f19_single_button_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static ssize_t rmi_f19_sensor_map_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t rmi_f19_sensor_map_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static ssize_t rmi_f19_sensitivity_adjust_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t rmi_f19_sensitivity_adjust_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static ssize_t rmi_f19_hysteresis_threshold_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t rmi_f19_hysteresis_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + + +static struct device_attribute attrs[] = { + __ATTR(button_count, RMI_RO_ATTR, + rmi_f19_button_count_show, rmi_store_error), + __ATTR(button_map, RMI_RW_ATTR, + rmi_f19_button_map_show, rmi_f19_button_map_store), + __ATTR(rezero, RMI_RW_ATTR, + rmi_f19_rezero_show, rmi_f19_rezero_store), + __ATTR(has_hysteresis_threshold, RMI_RO_ATTR, + rmi_f19_has_hysteresis_threshold_show, rmi_store_error), + __ATTR(has_sensitivity_adjust, RMI_RO_ATTR, + rmi_f19_has_sensitivity_adjust_show, rmi_store_error), + __ATTR(configurable, RMI_RO_ATTR, + rmi_f19_configurable_show, rmi_store_error), + __ATTR(filter_mode, RMI_RW_ATTR, + rmi_f19_filter_mode_show, rmi_f19_filter_mode_store), + __ATTR(button_usage, RMI_RW_ATTR, + rmi_f19_button_usage_show, rmi_f19_button_usage_store), + __ATTR(interrupt_enable_button, RMI_RW_ATTR, + rmi_f19_interrupt_enable_button_show, + rmi_f19_interrupt_enable_button_store), + __ATTR(single_button, RMI_RW_ATTR, + rmi_f19_single_button_show, rmi_f19_single_button_store), + __ATTR(sensor_map, RMI_RW_ATTR, + rmi_f19_sensor_map_show, rmi_f19_sensor_map_store), + __ATTR(sensitivity_adjust, RMI_RW_ATTR, + rmi_f19_sensitivity_adjust_show, + rmi_f19_sensitivity_adjust_store), + __ATTR(hysteresis_threshold, RMI_RW_ATTR, + rmi_f19_hysteresis_threshold_show, + rmi_f19_hysteresis_threshold_store) +}; + + +int rmi_f19_read_control_parameters(struct rmi_device *rmi_dev, + struct f19_0d_control *button_control, + unsigned char button_count, + unsigned char int_button_enabled_count, + u8 ctrl_base_addr) +{ + int error = 0; + int i; + + if (button_control->general_control) { + error = rmi_read_block(rmi_dev, ctrl_base_addr, + (u8 *)button_control->general_control, + sizeof(struct f19_0d_control_0)); + if (error < 0) { + dev_err(&rmi_dev->dev, + "Failed to read f19_0d_control_0, code:" + " %d.\n", error); + return error; + } + ctrl_base_addr = ctrl_base_addr + + sizeof(struct f19_0d_control_0); + } + + if (button_control->button_int_enable) { + for (i = 0; i < int_button_enabled_count; i++) { + error = rmi_read_block(rmi_dev, ctrl_base_addr, + (u8 *)&button_control->button_int_enable[i], + sizeof(struct f19_0d_control_1)); + if (error < 0) { + dev_err(&rmi_dev->dev, + "Failed to read f19_0d_control_2," + " code: %d.\n", error); + return error; + } + ctrl_base_addr = ctrl_base_addr + + sizeof(struct f19_0d_control_1); + } + } + + if (button_control->single_button_participation) { + for (i = 0; i < int_button_enabled_count; i++) { + error = rmi_read_block(rmi_dev, ctrl_base_addr, + (u8 *)&button_control-> + single_button_participation[i], + sizeof(struct f19_0d_control_2)); + if (error < 0) { + dev_err(&rmi_dev->dev, + "Failed to read f19_0d_control_2," + " code: %d.\n", error); + return error; + } + ctrl_base_addr = ctrl_base_addr + + sizeof(struct f19_0d_control_2); + } + } + + if (button_control->sensor_map) { + for (i = 0; i < button_count; i++) { + error = rmi_read_block(rmi_dev, ctrl_base_addr, + (u8 *)&button_control->sensor_map[i], + sizeof(struct f19_0d_control_3_4)); + if (error < 0) { + dev_err(&rmi_dev->dev, + "Failed to read f19_0d_control_3_4," + " code: %d.\n", error); + return error; + } + ctrl_base_addr = ctrl_base_addr + + sizeof(struct f19_0d_control_3_4); + } + } + + if (button_control->all_button_sensitivity_adj) { + error = rmi_read_block(rmi_dev, ctrl_base_addr, + (u8 *)button_control-> + all_button_sensitivity_adj, + sizeof(struct f19_0d_control_5)); + if (error < 0) { + dev_err(&rmi_dev->dev, + "Failed to read f19_0d_control_5," + " code: %d.\n", error); + return error; + } + ctrl_base_addr = ctrl_base_addr + + sizeof(struct f19_0d_control_5); + } + + if (button_control->all_button_hysteresis_threshold) { + error = rmi_read_block(rmi_dev, ctrl_base_addr, + (u8 *)button_control-> + all_button_hysteresis_threshold, + sizeof(struct f19_0d_control_6)); + if (error < 0) { + dev_err(&rmi_dev->dev, + "Failed to read f19_0d_control_6," + " code: %d.\n", error); + return error; + } + ctrl_base_addr = ctrl_base_addr + + sizeof(struct f19_0d_control_6); + } + return 0; +} + + +int rmi_f19_initialize_control_parameters(struct rmi_device *rmi_dev, + struct f19_0d_control *button_control, + unsigned char button_count, + unsigned char int_button_enabled_count, + int control_base_addr) +{ + int error = 0; + + button_control->general_control = + kzalloc(sizeof(struct f19_0d_control_0), GFP_KERNEL); + if (!button_control->general_control) { + dev_err(&rmi_dev->dev, "Failed to allocate" + " f19_0d_control_0.\n"); + error = -ENOMEM; + goto error_exit; + } + + button_control->button_int_enable = + kzalloc(int_button_enabled_count * + sizeof(struct f19_0d_control_2), GFP_KERNEL); + if (!button_control->button_int_enable) { + dev_err(&rmi_dev->dev, "Failed to allocate f19_0d_control_1.\n"); + error = -ENOMEM; + goto error_exit; + } + + button_control->single_button_participation = + kzalloc(int_button_enabled_count * + sizeof(struct f19_0d_control_2), GFP_KERNEL); + if (!button_control->single_button_participation) { + dev_err(&rmi_dev->dev, "Failed to allocate" + " f19_0d_control_2.\n"); + error = -ENOMEM; + goto error_exit; + } + + button_control->sensor_map = + kzalloc(button_count * + sizeof(struct f19_0d_control_3_4), GFP_KERNEL); + if (!button_control->sensor_map) { + dev_err(&rmi_dev->dev, "Failed to allocate" + " f19_0d_control_3_4.\n"); + error = -ENOMEM; + goto error_exit; + } + + button_control->all_button_sensitivity_adj = + kzalloc(sizeof(struct f19_0d_control_5), GFP_KERNEL); + if (!button_control->all_button_sensitivity_adj) { + dev_err(&rmi_dev->dev, "Failed to allocate" + " f19_0d_control_5.\n"); + error = -ENOMEM; + goto error_exit; + } + + button_control->all_button_hysteresis_threshold = + kzalloc(sizeof(struct f19_0d_control_6), GFP_KERNEL); + if (!button_control->all_button_hysteresis_threshold) { + dev_err(&rmi_dev->dev, "Failed to allocate" + " f19_0d_control_6.\n"); + error = -ENOMEM; + goto error_exit; + } + return rmi_f19_read_control_parameters(rmi_dev, button_control, + button_count, int_button_enabled_count, control_base_addr); + +error_exit: + kfree(button_control->general_control); + kfree(button_control->button_int_enable); + kfree(button_control->single_button_participation); + kfree(button_control->sensor_map); + kfree(button_control->all_button_sensitivity_adj); + kfree(button_control->all_button_hysteresis_threshold); + return error; +} + +static int rmi_f19_init(struct rmi_function_container *fc) +{ + struct rmi_device *rmi_dev = fc->rmi_dev; + struct rmi_device_platform_data *pdata; + struct f19_data *f19; + struct input_dev *input_dev; + u8 query_base_addr; + int rc; + int i; + int attr_count = 0; + + dev_info(&fc->dev, "Intializing F19 values."); + + f19 = kzalloc(sizeof(struct f19_data), GFP_KERNEL); + if (!f19) { + dev_err(&fc->dev, "Failed to allocate function data.\n"); + return -ENOMEM; + } + pdata = to_rmi_platform_data(rmi_dev); + query_base_addr = fc->fd.query_base_addr; + + /* initial all default values for f19 data here */ + rc = rmi_read(rmi_dev, fc->fd.command_base_addr, + (u8 *)&f19->button_rezero); + if (rc < 0) { + dev_err(&fc->dev, "Failed to read command register.\n"); + goto err_free_data; + } + + f19->button_rezero = f19->button_rezero & 1; + + rc = rmi_read_block(rmi_dev, query_base_addr, (u8 *)&f19->button_query, + sizeof(struct f19_0d_query)); + f19->button_count = f19->button_query.f19_0d_query1; + + if (rc < 0) { + dev_err(&fc->dev, "Failed to read query register.\n"); + goto err_free_data; + } + + + /* Figure out just how much data we'll need to read. */ + f19->button_down = kcalloc(f19->button_count, + sizeof(bool), GFP_KERNEL); + if (!f19->button_down) { + dev_err(&fc->dev, "Failed to allocate button state buffer.\n"); + rc = -ENOMEM; + goto err_free_data; + } + + f19->button_data_buffer_size = (f19->button_count + 7) / 8; + f19->button_data_buffer = + kcalloc(f19->button_data_buffer_size, + sizeof(unsigned char), GFP_KERNEL); + if (!f19->button_data_buffer) { + dev_err(&fc->dev, "Failed to allocate button data buffer.\n"); + rc = -ENOMEM; + goto err_free_data; + } + + f19->button_map = kcalloc(f19->button_count, + sizeof(unsigned char), GFP_KERNEL); + if (!f19->button_map) { + dev_err(&fc->dev, "Failed to allocate button map.\n"); + rc = -ENOMEM; + goto err_free_data; + } + + if (pdata) { + if (pdata->button_map->nbuttons != f19->button_count) { + dev_warn(&fc->dev, + "Platformdata button map size (%d) != number " + "of buttons on device (%d) - ignored.\n", + pdata->button_map->nbuttons, + f19->button_count); + } else if (!pdata->button_map->map) { + dev_warn(&fc->dev, + "Platformdata button map is missing!\n"); + } else { + for (i = 0; i < pdata->button_map->nbuttons; i++) + f19->button_map[i] = pdata->button_map->map[i]; + } + } + + f19->button_control = kzalloc(sizeof(struct f19_0d_control), + GFP_KERNEL); + + rc = rmi_f19_initialize_control_parameters(fc->rmi_dev, + f19->button_control, f19->button_count, + f19->button_data_buffer_size, fc->fd.control_base_addr); + if (rc < 0) { + dev_err(&fc->dev, + "Failed to initialize F19 control params.\n"); + goto err_free_data; + } + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&fc->dev, "Failed to allocate input device.\n"); + rc = -ENOMEM; + goto err_free_data; + } + + f19->input = input_dev; + snprintf(f19->input_name, MAX_LEN, "%sfn%02x", dev_name(&rmi_dev->dev), + fc->fd.function_number); + input_dev->name = f19->input_name; + snprintf(f19->input_phys, MAX_LEN, "%s/input0", input_dev->name); + input_dev->phys = f19->input_phys; + input_dev->dev.parent = &rmi_dev->dev; + input_set_drvdata(input_dev, f19); + + /* Set up any input events. */ + set_bit(EV_SYN, input_dev->evbit); + set_bit(EV_KEY, input_dev->evbit); + /* set bits for each button... */ + for (i = 0; i < f19->button_count; i++) + set_bit(f19->button_map[i], input_dev->keybit); + rc = input_register_device(input_dev); + if (rc < 0) { + dev_err(&fc->dev, "Failed to register input device.\n"); + goto err_free_input; + } + + dev_dbg(&fc->dev, "Creating sysfs files.\n"); + /* Set up sysfs device attributes. */ + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + if (sysfs_create_file + (&fc->dev.kobj, &attrs[attr_count].attr) < 0) { + dev_err(&fc->dev, + "Failed to create sysfs file for %s.", + attrs[attr_count].attr.name); + rc = -ENODEV; + goto err_free_data; + } + } + fc->data = f19; + return 0; + +err_free_input: + input_free_device(f19->input); + +err_free_data: + if (f19) { + kfree(f19->button_down); + kfree(f19->button_data_buffer); + kfree(f19->button_map); + } + kfree(f19); + for (attr_count--; attr_count >= 0; attr_count--) + sysfs_remove_file(&fc->dev.kobj, + &attrs[attr_count].attr); + return rc; +} + +int rmi_f19_attention(struct rmi_function_container *fc, u8 *irq_bits) +{ + struct rmi_device *rmi_dev = fc->rmi_dev; + struct f19_data *f19 = fc->data; + u8 data_base_addr = fc->fd.data_base_addr; + int error; + int button; + + /* Read the button data. */ + + error = rmi_read_block(rmi_dev, data_base_addr, f19->button_data_buffer, + f19->button_data_buffer_size); + if (error < 0) { + dev_err(&fc->dev, "%s: Failed to read button data registers.\n", + __func__); + return error; + } + + /* Generate events for buttons that change state. */ + for (button = 0; button < f19->button_count; + button++) { + int button_reg; + int button_shift; + bool button_status; + + /* determine which data byte the button status is in */ + button_reg = button / 7; + /* bit shift to get button's status */ + button_shift = button % 8; + button_status = + ((f19->button_data_buffer[button_reg] >> button_shift) + & 0x01) != 0; + + /* if the button state changed from the last time report it + * and store the new state */ + if (button_status != f19->button_down[button]) { + dev_dbg(&fc->dev, "%s: Button %d (code %d) -> %d.\n", + __func__, button, f19->button_map[button], + button_status); + /* Generate an event here. */ + input_report_key(f19->input, f19->button_map[button], + button_status); + f19->button_down[button] = button_status; + } + } + + input_sync(f19->input); /* sync after groups of events */ + return 0; +} + +static void rmi_f19_remove(struct rmi_function_container *fc) +{ + struct f19_data *data = fc->data; + if (data) { + kfree(data->button_down); + kfree(data->button_data_buffer); + kfree(data->button_map); + input_unregister_device(data->input); + if (data->button_control) { + kfree(data->button_control->general_control); + kfree(data->button_control->button_int_enable); + kfree(data->button_control-> + single_button_participation); + kfree(data->button_control->sensor_map); + kfree(data->button_control-> + all_button_sensitivity_adj); + kfree(data->button_control-> + all_button_hysteresis_threshold); + } + kfree(data->button_control); + } + kfree(fc->data); +} + +static struct rmi_function_handler function_handler = { + .func = 0x19, + .init = rmi_f19_init, + .attention = rmi_f19_attention, + .remove = rmi_f19_remove +}; + +static int __init rmi_f19_module_init(void) +{ + int error; + + error = rmi_register_function_driver(&function_handler); + if (error < 0) { + pr_err("%s: register failed!\n", __func__); + return error; + } + + return 0; +} + +static void rmi_f19_module_exit(void) +{ + rmi_unregister_function_driver(&function_handler); +} + +static ssize_t rmi_f19_filter_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct f19_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->button_control->general_control->filter_mode); + +} + +static ssize_t rmi_f19_filter_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rmi_function_container *fc; + struct f19_data *data; + unsigned int new_value; + int result; + + fc = to_rmi_function_container(dev); + data = fc->data; + if (sscanf(buf, "%u", &new_value) != 1) { + dev_err(dev, + "%s: Error - filter_mode_store has an " + "invalid len.\n", + __func__); + return -EINVAL; + } + + if (new_value < 0 || new_value > 4) { + dev_err(dev, "%s: Error - filter_mode_store has an " + "invalid value %d.\n", + __func__, new_value); + return -EINVAL; + } + data->button_control->general_control->filter_mode = new_value; + result = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr, + (u8 *)data->button_control->general_control, + sizeof(struct f19_0d_control_0)); + if (result < 0) { + dev_err(dev, "%s : Could not write filter_mode_store to 0x%x\n", + __func__, fc->fd.control_base_addr); + return result; + } + + return count; +} + +static ssize_t rmi_f19_button_usage_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct f19_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->button_control->general_control->button_usage); + +} + +static ssize_t rmi_f19_button_usage_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rmi_function_container *fc; + struct f19_data *data; + unsigned int new_value; + int result; + + fc = to_rmi_function_container(dev); + data = fc->data; + if (sscanf(buf, "%u", &new_value) != 1) { + dev_err(dev, + "%s: Error - button_usage_store has an " + "invalid len.\n", + __func__); + return -EINVAL; + } + + if (new_value < 0 || new_value > 4) { + dev_err(dev, "%s: Error - button_usage_store has an " + "invalid value %d.\n", + __func__, new_value); + return -EINVAL; + } + data->button_control->general_control->button_usage = new_value; + result = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr, + (u8 *)data->button_control->general_control, + sizeof(struct f19_0d_control_0)); + if (result < 0) { + dev_err(dev, "%s : Could not write button_usage_store to 0x%x\n", + __func__, fc->fd.control_base_addr); + return result; + } + + return count; + +} + +static ssize_t rmi_f19_interrupt_enable_button_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct f19_data *data; + int i, len, total_len = 0; + char *current_buf = buf; + + fc = to_rmi_function_container(dev); + data = fc->data; + /* loop through each button map value and copy its + * string representation into buf */ + for (i = 0; i < data->button_count; i++) { + int button_reg; + int button_shift; + int interrupt_button; + + button_reg = i / 7; + button_shift = i % 8; + interrupt_button = + ((data->button_control-> + button_int_enable[button_reg].int_enabled_button >> + button_shift) & 0x01); + + /* get next button mapping value and write it to buf */ + len = snprintf(current_buf, PAGE_SIZE - total_len, + "%u ", interrupt_button); + /* bump up ptr to next location in buf if the + * snprintf was valid. Otherwise issue an error + * and return. */ + if (len > 0) { + current_buf += len; + total_len += len; + } else { + dev_err(dev, "%s: Failed to build interrupt button" + " buffer, code = %d.\n", __func__, len); + return snprintf(buf, PAGE_SIZE, "unknown\n"); + } + } + len = snprintf(current_buf, PAGE_SIZE - total_len, "\n"); + if (len > 0) + total_len += len; + else + dev_warn(dev, "%s: Failed to append carriage return.\n", + __func__); + return total_len; + +} + +static ssize_t rmi_f19_interrupt_enable_button_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rmi_function_container *fc; + struct f19_data *data; + int i; + int button_count = 0; + int retval = count; + int button_reg = 0; + int ctrl_bass_addr; + + fc = to_rmi_function_container(dev); + data = fc->data; + for (i = 0; i < data->button_count && *buf != 0; + i++) { + int button_shift; + int button; + + button_reg = i / 7; + button_shift = i % 8; + /* get next button mapping value and store and bump up to + * point to next item in buf */ + sscanf(buf, "%u", &button); + + if (button != 0 && button != 1) { + dev_err(dev, + "%s: Error - interrupt enable button for" + " button %d is not a valid value 0x%x.\n", + __func__, i, button); + return -EINVAL; + } + + if (button_shift == 0) + data->button_control->button_int_enable[button_reg]. + int_enabled_button = 0; + data->button_control->button_int_enable[button_reg]. + int_enabled_button |= (button << button_shift); + button_count++; + /* bump up buf to point to next item to read */ + while (*buf != 0) { + buf++; + if (*(buf - 1) == ' ') + break; + } + } + + /* Make sure the button count matches */ + if (button_count != data->button_count) { + dev_err(dev, + "%s: Error - interrupt enable button count of %d" + " doesn't match device button count of %d.\n", + __func__, button_count, data->button_count); + return -EINVAL; + } + + /* write back to the control register */ + ctrl_bass_addr = fc->fd.control_base_addr + + sizeof(struct f19_0d_control_0); + retval = rmi_write_block(fc->rmi_dev, ctrl_bass_addr, + (u8 *)data->button_control->button_int_enable, + sizeof(struct f19_0d_control_1)*(button_reg + 1)); + if (retval < 0) { + dev_err(dev, "%s : Could not write interrupt_enable_store" + " to 0x%x\n", __func__, ctrl_bass_addr); + return retval; + } + + return count; +} + +static ssize_t rmi_f19_single_button_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct f19_data *data; + int i, len, total_len = 0; + char *current_buf = buf; + + fc = to_rmi_function_container(dev); + data = fc->data; + /* loop through each button map value and copy its + * string representation into buf */ + for (i = 0; i < data->button_count; i++) { + int button_reg; + int button_shift; + int single_button; + + button_reg = i / 7; + button_shift = i % 8; + single_button = ((data->button_control-> + single_button_participation[button_reg].single_button + >> button_shift) & 0x01); + + /* get next button mapping value and write it to buf */ + len = snprintf(current_buf, PAGE_SIZE - total_len, + "%u ", single_button); + /* bump up ptr to next location in buf if the + * snprintf was valid. Otherwise issue an error + * and return. */ + if (len > 0) { + current_buf += len; + total_len += len; + } else { + dev_err(dev, "%s: Failed to build signle button buffer" + ", code = %d.\n", __func__, len); + return snprintf(buf, PAGE_SIZE, "unknown\n"); + } + } + len = snprintf(current_buf, PAGE_SIZE - total_len, "\n"); + if (len > 0) + total_len += len; + else + dev_warn(dev, "%s: Failed to append carriage return.\n", + __func__); + + return total_len; + +} + +static ssize_t rmi_f19_single_button_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rmi_function_container *fc; + struct f19_data *data; + int i; + int button_count = 0; + int retval = count; + int ctrl_bass_addr; + int button_reg = 0; + + fc = to_rmi_function_container(dev); + data = fc->data; + for (i = 0; i < data->button_count && *buf != 0; + i++) { + int button_shift; + int button; + + button_reg = i / 7; + button_shift = i % 8; + /* get next button mapping value and store and bump up to + * point to next item in buf */ + sscanf(buf, "%u", &button); + + if (button != 0 && button != 1) { + dev_err(dev, + "%s: Error - single button for button %d" + " is not a valid value 0x%x.\n", + __func__, i, button); + return -EINVAL; + } + if (button_shift == 0) + data->button_control-> + single_button_participation[button_reg]. + single_button = 0; + data->button_control->single_button_participation[button_reg]. + single_button |= (button << button_shift); + button_count++; + /* bump up buf to point to next item to read */ + while (*buf != 0) { + buf++; + if (*(buf - 1) == ' ') + break; + } + } + + /* Make sure the button count matches */ + if (button_count != data->button_count) { + dev_err(dev, + "%s: Error - single button count of %d doesn't match" + " device button count of %d.\n", __func__, button_count, + data->button_count); + return -EINVAL; + } + /* write back to the control register */ + ctrl_bass_addr = fc->fd.control_base_addr + + sizeof(struct f19_0d_control_0) + + sizeof(struct f19_0d_control_2)*(button_reg + 1); + retval = rmi_write_block(fc->rmi_dev, ctrl_bass_addr, + (u8 *)data->button_control->single_button_participation, + sizeof(struct f19_0d_control_2)*(button_reg + 1)); + if (retval < 0) { + dev_err(dev, "%s : Could not write interrupt_enable_store to" + " 0x%x\n", __func__, ctrl_bass_addr); + return -EINVAL; + } + return count; +} + +static ssize_t rmi_f19_sensor_map_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct f19_data *data; + int i, len, total_len = 0; + char *current_buf = buf; + + fc = to_rmi_function_container(dev); + data = fc->data; + + for (i = 0; i < data->button_count; i++) { + len = snprintf(current_buf, PAGE_SIZE - total_len, + "%u ", data->button_control->sensor_map[i]. + sensor_map_button); + /* bump up ptr to next location in buf if the + * snprintf was valid. Otherwise issue an error + * and return. */ + if (len > 0) { + current_buf += len; + total_len += len; + } else { + dev_err(dev, "%s: Failed to build sensor map buffer, " + "code = %d.\n", __func__, len); + return snprintf(buf, PAGE_SIZE, "unknown\n"); + } + } + len = snprintf(current_buf, PAGE_SIZE - total_len, "\n"); + if (len > 0) + total_len += len; + else + dev_warn(dev, "%s: Failed to append carriage return.\n", + __func__); + return total_len; + + +} + +static ssize_t rmi_f19_sensor_map_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rmi_function_container *fc; + struct f19_data *data; + int sensor_map; + int i; + int retval = count; + int button_count = 0; + int ctrl_bass_addr; + int button_reg; + fc = to_rmi_function_container(dev); + data = fc->data; + + if (data->button_query.configurable == 0) { + dev_err(dev, + "%s: Error - sensor map is not configuralbe at" + " run-time", __func__); + return -EINVAL; + } + + for (i = 0; i < data->button_count && *buf != 0; i++) { + /* get next button mapping value and store and bump up to + * point to next item in buf */ + sscanf(buf, "%u", &sensor_map); + + /* Make sure the key is a valid key */ + if (sensor_map < 0 || sensor_map > 127) { + dev_err(dev, + "%s: Error - sensor map for button %d is" + " not a valid value 0x%x.\n", + __func__, i, sensor_map); + return -EINVAL; + } + + data->button_control->sensor_map[i].sensor_map_button = + sensor_map; + button_count++; + + /* bump up buf to point to next item to read */ + while (*buf != 0) { + buf++; + if (*(buf - 1) == ' ') + break; + } + } + + if (button_count != data->button_count) { + dev_err(dev, + "%s: Error - button map count of %d doesn't match device " + "button count of %d.\n", __func__, button_count, + data->button_count); + return -EINVAL; + } + + /* write back to the control register */ + button_reg = (button_count / 7) + 1; + ctrl_bass_addr = fc->fd.control_base_addr + + sizeof(struct f19_0d_control_0) + + sizeof(struct f19_0d_control_1)*button_reg + + sizeof(struct f19_0d_control_2)*button_reg; + retval = rmi_write_block(fc->rmi_dev, ctrl_bass_addr, + (u8 *)data->button_control->sensor_map, + sizeof(struct f19_0d_control_3_4)*button_count); + if (retval < 0) { + dev_err(dev, "%s : Could not sensor_map_store to 0x%x\n", + __func__, ctrl_bass_addr); + return -EINVAL; + } + return count; +} + +static ssize_t rmi_f19_sensitivity_adjust_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct f19_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", data->button_control-> + all_button_sensitivity_adj->sensitivity_adj); + +} + +static ssize_t rmi_f19_sensitivity_adjust_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rmi_function_container *fc; + struct f19_data *data; + unsigned int new_value; + int len; + int ctrl_bass_addr; + int button_reg; + + fc = to_rmi_function_container(dev); + + data = fc->data; + + if (data->button_query.configurable == 0) { + dev_err(dev, + "%s: Error - sensitivity_adjust is not" + " configuralbe at run-time", __func__); + return -EINVAL; + } + + len = sscanf(buf, "%u", &new_value); + if (new_value < 0 || new_value > 31) + return -EINVAL; + + data->button_control->all_button_sensitivity_adj->sensitivity_adj = + new_value; + /* write back to the control register */ + button_reg = (data->button_count / 7) + 1; + ctrl_bass_addr = fc->fd.control_base_addr + + sizeof(struct f19_0d_control_0) + + sizeof(struct f19_0d_control_1)*button_reg + + sizeof(struct f19_0d_control_2)*button_reg + + sizeof(struct f19_0d_control_3_4)*data->button_count; + len = rmi_write_block(fc->rmi_dev, ctrl_bass_addr, + (u8 *)data->button_control->all_button_sensitivity_adj, + sizeof(struct f19_0d_control_5)); + if (len < 0) { + dev_err(dev, "%s : Could not sensitivity_adjust_store to" + " 0x%x\n", __func__, ctrl_bass_addr); + return len; + } + + return len; +} + +static ssize_t rmi_f19_hysteresis_threshold_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct f19_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", data->button_control-> + all_button_hysteresis_threshold->hysteresis_threshold); + +} +static ssize_t rmi_f19_hysteresis_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rmi_function_container *fc; + struct f19_data *data; + unsigned int new_value; + int len; + int ctrl_bass_addr; + int button_reg; + + fc = to_rmi_function_container(dev); + data = fc->data; + len = sscanf(buf, "%u", &new_value); + if (new_value < 0 || new_value > 15) { + dev_err(dev, "%s: Error - hysteresis_threshold_store has an " + "invalid value %d.\n", + __func__, new_value); + return -EINVAL; + } + data->button_control->all_button_hysteresis_threshold-> + hysteresis_threshold = new_value; + /* write back to the control register */ + button_reg = (data->button_count / 7) + 1; + ctrl_bass_addr = fc->fd.control_base_addr + + sizeof(struct f19_0d_control_0) + + sizeof(struct f19_0d_control_1)*button_reg + + sizeof(struct f19_0d_control_2)*button_reg + + sizeof(struct f19_0d_control_3_4)*data->button_count+ + sizeof(struct f19_0d_control_5); + len = rmi_write_block(fc->rmi_dev, ctrl_bass_addr, + (u8 *)data->button_control->all_button_sensitivity_adj, + sizeof(struct f19_0d_control_6)); + if (len < 0) { + dev_err(dev, "%s : Could not write all_button hysteresis " + "threshold to 0x%x\n", __func__, ctrl_bass_addr); + return -EINVAL; + } + + return count; +} + +static ssize_t rmi_f19_has_hysteresis_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rmi_function_container *fc; + struct f19_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->button_query.has_hysteresis_threshold); +} + +static ssize_t rmi_f19_has_sensitivity_adjust_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rmi_function_container *fc; + struct f19_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->button_query.has_sensitivity_adjust); +} + +static ssize_t rmi_f19_configurable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rmi_function_container *fc; + struct f19_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->button_query.configurable); +} + +static ssize_t rmi_f19_rezero_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct f19_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->button_rezero); + +} + +static ssize_t rmi_f19_rezero_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct rmi_function_container *fc; + struct f19_data *data; + unsigned int new_value; + int len; + + fc = to_rmi_function_container(dev); + data = fc->data; + len = sscanf(buf, "%u", &new_value); + if (new_value != 0 && new_value != 1) { + dev_err(dev, + "%s: Error - rezero is not a " + "valid value 0x%x.\n", + __func__, new_value); + return -EINVAL; + } + data->button_rezero = new_value & 1; + len = rmi_write(fc->rmi_dev, fc->fd.command_base_addr, + data->button_rezero); + + if (len < 0) { + dev_err(dev, "%s : Could not write rezero to 0x%x\n", + __func__, fc->fd.command_base_addr); + return -EINVAL; + } + return count; +} + +static ssize_t rmi_f19_button_count_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct f19_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + return snprintf(buf, PAGE_SIZE, "%u\n", + data->button_count); +} + +static ssize_t rmi_f19_button_map_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + + struct rmi_function_container *fc; + struct f19_data *data; + int i, len, total_len = 0; + char *current_buf = buf; + + fc = to_rmi_function_container(dev); + data = fc->data; + /* loop through each button map value and copy its + * string representation into buf */ + for (i = 0; i < data->button_count; i++) { + /* get next button mapping value and write it to buf */ + len = snprintf(current_buf, PAGE_SIZE - total_len, + "%u ", data->button_map[i]); + /* bump up ptr to next location in buf if the + * snprintf was valid. Otherwise issue an error + * and return. */ + if (len > 0) { + current_buf += len; + total_len += len; + } else { + dev_err(dev, "%s: Failed to build button map buffer, " + "code = %d.\n", __func__, len); + return snprintf(buf, PAGE_SIZE, "unknown\n"); + } + } + len = snprintf(current_buf, PAGE_SIZE - total_len, "\n"); + if (len > 0) + total_len += len; + else + dev_warn(dev, "%s: Failed to append carriage return.\n", + __func__); + return total_len; +} + +static ssize_t rmi_f19_button_map_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct rmi_function_container *fc; + struct f19_data *data; + unsigned int button; + int i; + int retval = count; + int button_count = 0; + unsigned char temp_button_map[KEY_MAX]; + + fc = to_rmi_function_container(dev); + data = fc->data; + + /* Do validation on the button map data passed in. Store button + * mappings into a temp buffer and then verify button count and + * data prior to clearing out old button mappings and storing the + * new ones. */ + for (i = 0; i < data->button_count && *buf != 0; + i++) { + /* get next button mapping value and store and bump up to + * point to next item in buf */ + sscanf(buf, "%u", &button); + + /* Make sure the key is a valid key */ + if (button > KEY_MAX) { + dev_err(dev, + "%s: Error - button map for button %d is not a" + " valid value 0x%x.\n", __func__, i, button); + retval = -EINVAL; + goto err_ret; + } + + temp_button_map[i] = button; + button_count++; + + /* bump up buf to point to next item to read */ + while (*buf != 0) { + buf++; + if (*(buf - 1) == ' ') + break; + } + } + + /* Make sure the button count matches */ + if (button_count != data->button_count) { + dev_err(dev, + "%s: Error - button map count of %d doesn't match device " + "button count of %d.\n", __func__, button_count, + data->button_count); + retval = -EINVAL; + goto err_ret; + } + + /* Clear the key bits for the old button map. */ + for (i = 0; i < button_count; i++) + clear_bit(data->button_map[i], data->input->keybit); + + /* Switch to the new map. */ + memcpy(data->button_map, temp_button_map, + data->button_count); + + /* Loop through the key map and set the key bit for the new mapping. */ + for (i = 0; i < button_count; i++) + set_bit(data->button_map[i], data->input->keybit); + +err_ret: + return retval; +} + +module_init(rmi_f19_module_init); +module_exit(rmi_f19_module_exit); + +MODULE_AUTHOR("Vivian Ly "); +MODULE_DESCRIPTION("RMI F19 module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/rmi4/rmi_f34.c b/drivers/input/touchscreen/rmi4/rmi_f34.c new file mode 100644 index 000000000000..33e84d2cfb24 --- /dev/null +++ b/drivers/input/touchscreen/rmi4/rmi_f34.c @@ -0,0 +1,821 @@ +/* + * Copyright (c) 2011 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include "rmi_driver.h" + +/* define fn $34 commands */ +#define WRITE_FW_BLOCK 0x2 +#define ERASE_ALL 0x3 +#define READ_CONFIG_BLOCK 0x5 +#define WRITE_CONFIG_BLOCK 0x6 +#define ERASE_CONFIG 0x7 +#define ENABLE_FLASH_PROG 0xf + +#define STATUS_IN_PROGRESS 0xff +#define STATUS_IDLE 0x80 + +#define PDT_START_SCAN_LOCATION 0x00e9 +#define PDT_END_SCAN_LOCATION 0x0005 + +#define BLK_SZ_OFF 3 +#define IMG_BLK_CNT_OFF 5 +#define CFG_BLK_CNT_OFF 7 + +#define BLK_NUM_OFF 2 + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) +#define KERNEL_VERSION_ABOVE_2_6_32 1 +#endif + +/* data specific to fn $34 that needs to be kept around */ +struct rmi_fn_34_data { + unsigned char status; + unsigned char cmd; + unsigned short bootloaderid; + unsigned short blocksize; + unsigned short imageblockcount; + unsigned short configblockcount; + unsigned short blocknum; + bool inflashprogmode; +}; + +static ssize_t rmi_fn_34_status_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_34_cmd_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_34_cmd_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +#ifdef KERNEL_VERSION_ABOVE_2_6_32 +static ssize_t rmi_fn_34_data_read(struct file *data_file, struct kobject *kobj, + struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t rmi_fn_34_data_write(struct file *data_file, + struct kobject *kobj, + struct bin_attribute *attributes, char *buf, + loff_t pos, size_t count); +#else +static ssize_t rmi_fn_34_data_read(struct kobject *kobj, + struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t rmi_fn_34_data_write(struct kobject *kobj, + struct bin_attribute *attributes, char *buf, + loff_t pos, size_t count); +#endif + +static ssize_t rmi_fn_34_bootloaderid_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_fn_34_bootloaderid_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static ssize_t rmi_fn_34_blocksize_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_fn_34_imageblockcount_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_fn_34_configblockcount_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_fn_34_blocknum_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t rmi_fn_34_blocknum_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static ssize_t rmi_fn_34_rescanPDT_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static struct device_attribute attrs[] = { + __ATTR(status, RMI_RO_ATTR, + rmi_fn_34_status_show, rmi_store_error), + /* Also, sysfs will need to have a file set up to distinguish + * between commands - like Config write/read, Image write/verify. */ + __ATTR(cmd, RMI_RW_ATTR, + rmi_fn_34_cmd_show, rmi_fn_34_cmd_store), + __ATTR(bootloaderid, RMI_RW_ATTR, + rmi_fn_34_bootloaderid_show, rmi_fn_34_bootloaderid_store), + __ATTR(blocksize, RMI_RO_ATTR, + rmi_fn_34_blocksize_show, rmi_store_error), + __ATTR(imageblockcount, RMI_RO_ATTR, + rmi_fn_34_imageblockcount_show, rmi_store_error), + __ATTR(configblockcount, RMI_RO_ATTR, + rmi_fn_34_configblockcount_show, rmi_store_error), + __ATTR(blocknum, RMI_RW_ATTR, + rmi_fn_34_blocknum_show, rmi_fn_34_blocknum_store), + __ATTR(rescanPDT, RMI_WO_ATTR, + rmi_show_error, rmi_fn_34_rescanPDT_store) +}; + +struct bin_attribute dev_attr_data = { + .attr = { + .name = "data", + .mode = 0666}, + .size = 0, + .read = rmi_fn_34_data_read, + .write = rmi_fn_34_data_write, +}; + +static int rmi_f34_init(struct rmi_function_container *fc) +{ + int retval = 0; + int attr_count = 0; + struct rmi_fn_34_data *f34; + u16 query_base_addr; + u16 control_base_addr; + unsigned char buf[2]; + + dev_info(&fc->dev, "Intializing f34 values."); + + /* init instance data, fill in values and create any sysfs files */ + f34 = kzalloc(sizeof(struct rmi_fn_34_data), GFP_KERNEL); + if (!f34) { + dev_err(&fc->dev, "Failed to allocate rmi_fn_34_data.\n"); + return -ENOMEM; + } + + fc->data = f34; + + /* get the Bootloader ID and Block Size. */ + query_base_addr = fc->fd.query_base_addr; + control_base_addr = fc->fd.control_base_addr; + + retval = rmi_read_block(fc->rmi_dev, query_base_addr, buf, + ARRAY_SIZE(buf)); + + if (retval < 0) { + dev_err(&fc->dev, "Could not read bootloaderid from 0x%04x.\n", + query_base_addr); + goto exit_free_data; + } + batohs(&f34->bootloaderid, buf); + + retval = rmi_read_block(fc->rmi_dev, query_base_addr + BLK_SZ_OFF, buf, + ARRAY_SIZE(buf)); + + if (retval < 0) { + dev_err(&fc->dev, "Could not read block size from 0x%04x, " + "error=%d.\n", query_base_addr + BLK_SZ_OFF, retval); + goto exit_free_data; + } + batohs(&f34->blocksize, buf); + + /* Get firmware image block count and store it in the instance data */ + retval = rmi_read_block(fc->rmi_dev, query_base_addr + IMG_BLK_CNT_OFF, + buf, ARRAY_SIZE(buf)); + + if (retval < 0) { + dev_err(&fc->dev, "Couldn't read image block count from 0x%x, " + "error=%d.\n", query_base_addr + IMG_BLK_CNT_OFF, + retval); + goto exit_free_data; + } + batohs(&f34->imageblockcount, buf); + + /* Get config block count and store it in the instance data */ + retval = rmi_read_block(fc->rmi_dev, query_base_addr + 7, buf, + ARRAY_SIZE(buf)); + + if (retval < 0) { + dev_err(&fc->dev, "Couldn't read config block count from 0x%x, " + "error=%d.\n", query_base_addr + CFG_BLK_CNT_OFF, + retval); + goto exit_free_data; + } + batohs(&f34->configblockcount, buf); + + /* We need a sysfs file for the image/config block to write or read. + * Set up sysfs bin file for binary data block. Since the image is + * already in our format there is no need to convert the data for + * endianess. */ + retval = sysfs_create_bin_file(&fc->dev.kobj, + &dev_attr_data); + if (retval < 0) { + dev_err(&fc->dev, "Failed to create sysfs file for F34 data " + "(error = %d).\n", retval); + retval = -ENODEV; + goto exit_free_data; + } + + dev_dbg(&fc->dev, "Creating sysfs files.\n"); + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + if (sysfs_create_file + (&fc->dev.kobj, &attrs[attr_count].attr) < 0) { + dev_err(&fc->dev, "Failed to create sysfs file for %s.", + attrs[attr_count].attr.name); + retval = -ENODEV; + goto exit_free_attrs; + } + } + + return retval; + +exit_free_attrs: + for (attr_count--; attr_count >= 0; attr_count--) + sysfs_remove_file(&fc->dev.kobj, + &attrs[attr_count].attr); +exit_free_data: + kfree(f34); + return retval; +} + +static int f34_read_status(struct rmi_function_container *fc) +{ + struct rmi_fn_34_data *instance_data = fc->data; + u16 data_base_addr = fc->fd.data_base_addr; + u8 status; + int retval; + + /* Read the Fn $34 status from F34_Flash_Data3 to see the previous + * commands status. F34_Flash_Data3 will be the address after the + * 2 block number registers plus blocksize Data registers. + * inform user space - through a sysfs param. */ + retval = rmi_read(fc->rmi_dev, + data_base_addr + instance_data->blocksize + + BLK_NUM_OFF, &status); + + if (retval < 0) { + dev_err(&fc->dev, "Could not read status from 0x%x\n", + data_base_addr + instance_data->blocksize + BLK_NUM_OFF); + status = 0xff; /* failure */ + } + + /* set a sysfs value that the user mode can read - only + * upper 4 bits are the status. successful is $80, anything + * else is failure */ + instance_data->status = status & 0xf0; + + /* put mode into Flash Prog Mode when we successfully do + * an Enable Flash Prog cmd. */ + if ((instance_data->status == STATUS_IDLE) && + (instance_data->cmd == ENABLE_FLASH_PROG)) + instance_data->inflashprogmode = true; + + return retval; +} + +int rmi_f34_attention(struct rmi_function_container *fc, u8 *irq_bits) +{ + return f34_read_status(fc); +} + +static struct rmi_function_handler function_handler = { + .func = 0x34, + .init = rmi_f34_init, + .attention = rmi_f34_attention +}; + +static ssize_t rmi_fn_34_bootloaderid_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct rmi_fn_34_data *instance_data; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->bootloaderid); +} + +static ssize_t rmi_fn_34_bootloaderid_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int error; + unsigned long val; + unsigned char data[2]; + struct rmi_function_container *fc; + struct rmi_fn_34_data *instance_data; + u16 data_base_addr; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + /* need to convert the string data to an actual value */ + error = strict_strtoul(buf, 10, &val); + + if (error) + return error; + + instance_data->bootloaderid = val; + + /* Write the Bootloader ID key data back to the first two Block + * Data registers (F34_Flash_Data2.0 and F34_Flash_Data2.1). */ + hstoba(data, (unsigned short)val); + data_base_addr = fc->fd.data_base_addr; + + error = rmi_write_block(fc->rmi_dev, + data_base_addr + BLK_NUM_OFF, + data, + ARRAY_SIZE(data)); + + if (error < 0) { + dev_err(dev, "%s : Could not write bootloader id to 0x%x\n", + __func__, data_base_addr + BLK_NUM_OFF); + return error; + } + + return count; +} + +static ssize_t rmi_fn_34_blocksize_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct rmi_fn_34_data *instance_data; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->blocksize); +} + +static ssize_t rmi_fn_34_imageblockcount_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct rmi_fn_34_data *instance_data; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + instance_data->imageblockcount); +} + +static ssize_t rmi_fn_34_configblockcount_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct rmi_fn_34_data *instance_data; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + instance_data->configblockcount); +} + +static ssize_t rmi_fn_34_status_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct rmi_fn_34_data *instance_data; + int retval; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + retval = f34_read_status(fc); + + return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->status); +} + +static ssize_t rmi_fn_34_cmd_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct rmi_fn_34_data *instance_data; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->cmd); +} + +static ssize_t rmi_fn_34_cmd_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct rmi_function_container *fc; + struct rmi_fn_34_data *instance_data; + unsigned long val; + u16 data_base_addr; + int error; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + data_base_addr = fc->fd.data_base_addr; + + /* need to convert the string data to an actual value */ + error = strict_strtoul(buf, 10, &val); + if (error) + return error; + + /* make sure we are in Flash Prog mode for all cmds except the + * Enable Flash Programming cmd - otherwise we are in error */ + if ((val != ENABLE_FLASH_PROG) && !instance_data->inflashprogmode) { + dev_err(dev, "%s: CANNOT SEND CMD %d TO SENSOR - " + "NOT IN FLASH PROG MODE\n" + , __func__, data_base_addr); + return -EINVAL; + } + + instance_data->cmd = val; + + /* Validate command value and (if necessary) write it to the command + * register. + */ + switch (instance_data->cmd) { + case ENABLE_FLASH_PROG: + case ERASE_ALL: + case ERASE_CONFIG: + case WRITE_FW_BLOCK: + case READ_CONFIG_BLOCK: + case WRITE_CONFIG_BLOCK: + /* Reset the status to indicate we are in progress on a cmd. */ + /* The status will change when the ATTN interrupt happens + and the status of the cmd that was issued is read from + the F34_Flash_Data3 register - result should be 0x80 for + success - any other value indicates an error */ + + /* Issue the command to the device. */ + error = rmi_write(fc->rmi_dev, + data_base_addr + instance_data->blocksize + + BLK_NUM_OFF, instance_data->cmd); + + if (error < 0) { + dev_err(dev, "%s: Could not write command 0x%02x " + "to 0x%04x\n", __func__, instance_data->cmd, + data_base_addr + instance_data->blocksize + + BLK_NUM_OFF); + return error; + } + + if (instance_data->cmd == ENABLE_FLASH_PROG) + instance_data->inflashprogmode = true; + + /* set status to indicate we are in progress */ + instance_data->status = STATUS_IN_PROGRESS; + break; + default: + dev_dbg(dev, "%s: RMI4 function $34 - " + "unknown command 0x%02lx.\n", __func__, val); + count = -EINVAL; + break; + } + + return count; +} + +static ssize_t rmi_fn_34_blocknum_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct rmi_fn_34_data *instance_data; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->blocknum); +} + +static ssize_t rmi_fn_34_blocknum_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int error; + unsigned long val; + unsigned char data[2]; + struct rmi_function_container *fc; + struct rmi_fn_34_data *instance_data; + u16 data_base_addr; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + data_base_addr = fc->fd.data_base_addr; + + /* need to convert the string data to an actual value */ + error = strict_strtoul(buf, 10, &val); + + if (error) + return error; + + instance_data->blocknum = val; + + /* Write the Block Number data back to the first two Block + * Data registers (F34_Flash_Data_0 and F34_Flash_Data_1). */ + hstoba(data, (unsigned short)val); + + error = rmi_write_block(fc->rmi_dev, + data_base_addr, + data, + ARRAY_SIZE(data)); + + if (error < 0) { + dev_err(dev, "%s : Could not write block number %u to 0x%x\n", + __func__, instance_data->blocknum, data_base_addr); + return error; + } + + return count; +} + +static ssize_t rmi_fn_34_rescanPDT_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rmi_function_container *fc; + struct rmi_fn_34_data *instance_data; + struct rmi_device *rmi_dev; + struct rmi_driver_data *driver_data; + struct pdt_entry pdt_entry; + bool fn01found = false; + bool fn34found = false; + unsigned int rescan; + int irq_count = 0; + int retval = 0; + int i; + + /* Rescan of the PDT is needed since issuing the Flash Enable cmd + * the device registers for Fn$01 and Fn$34 moving around because + * of the change from Bootloader mode to Flash Programming mode + * may change to a different PDT with only Fn$01 and Fn$34 that + * could have addresses for query, control, data, command registers + * that differ from the PDT scan done at device initialization. */ + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + rmi_dev = fc->rmi_dev; + driver_data = rmi_get_driverdata(rmi_dev); + + /* Make sure we are only in Flash Programming mode - DON'T + * ALLOW THIS IN UI MODE. */ + if (instance_data->cmd != ENABLE_FLASH_PROG) { + dev_err(dev, "%s: NOT IN FLASH PROG MODE - CAN'T RESCAN PDT.\n" + , __func__); + return -EINVAL; + } + + /* The only good value to write to this is 1, we allow 0, but with + * no effect (this is consistent with the way the command bit works. */ + if (sscanf(buf, "%u", &rescan) != 1) + return -EINVAL; + if (rescan < 0 || rescan > 1) + return -EINVAL; + + /* 0 has no effect, so we skip it entirely. */ + if (rescan) { + /* rescan the PDT - filling in Fn01 and Fn34 addresses - + * this is only temporary - the device will need to be reset + * to return the PDT to the normal values. */ + + /* mini-parse the PDT - we only have to get Fn$01 and Fn$34 and + since we are Flash Programming mode we only have page 0. */ + for (i = PDT_START_SCAN_LOCATION; i >= PDT_END_SCAN_LOCATION; + i -= sizeof(pdt_entry)) { + retval = rmi_read_block(rmi_dev, i, (u8 *)&pdt_entry, + sizeof(pdt_entry)); + if (retval != sizeof(pdt_entry)) { + dev_err(dev, "%s: err frm rmi_read_block pdt " + "entry data from PDT, " + "error = %d.", __func__, retval); + return retval; + } + + if ((pdt_entry.function_number == 0x00) || + (pdt_entry.function_number == 0xff)) + break; + + dev_dbg(dev, "%s: Found F%.2X\n", + __func__, pdt_entry.function_number); + + /* f01 found - just fill in the new addresses in + * the existing fc. */ + if (pdt_entry.function_number == 0x01) { + struct rmi_function_container *f01_fc = + driver_data->f01_container; + fn01found = true; + f01_fc->fd.query_base_addr = + pdt_entry.query_base_addr; + f01_fc->fd.command_base_addr = + pdt_entry.command_base_addr; + f01_fc->fd.control_base_addr = + pdt_entry.control_base_addr; + f01_fc->fd.data_base_addr = + pdt_entry.data_base_addr; + f01_fc->fd.function_number = + pdt_entry.function_number; + f01_fc->fd.interrupt_source_count = + pdt_entry.interrupt_source_count; + f01_fc->num_of_irqs = + pdt_entry.interrupt_source_count; + f01_fc->irq_pos = irq_count; + + irq_count += f01_fc->num_of_irqs; + + if (fn34found) + break; + } + + /* f34 found - just fill in the new addresses in + * the existing fc. */ + if (pdt_entry.function_number == 0x34) { + fn34found = true; + fc->fd.query_base_addr = + pdt_entry.query_base_addr; + fc->fd.command_base_addr = + pdt_entry.command_base_addr; + fc->fd.control_base_addr = + pdt_entry.control_base_addr; + fc->fd.data_base_addr = + pdt_entry.data_base_addr; + fc->fd.function_number = + pdt_entry.function_number; + fc->fd.interrupt_source_count = + pdt_entry.interrupt_source_count; + fc->num_of_irqs = + pdt_entry.interrupt_source_count; + fc->irq_pos = irq_count; + + irq_count += fc->num_of_irqs; + + if (fn01found) + break; + } + + } + + if (!fn01found || !fn34found) { + dev_err(dev, "%s: failed to find fn$01 or fn$34 trying " + "to do rescan PDT.\n" + , __func__); + return -EINVAL; + } + } + + return count; +} + +#ifdef KERNEL_VERSION_ABOVE_2_6_32 +static ssize_t rmi_fn_34_data_read(struct file *data_file, + struct kobject *kobj, + struct bin_attribute *attributes, + char *buf, + loff_t pos, + size_t count) +#else +static ssize_t rmi_fn_34_data_read(struct kobject *kobj, + struct bin_attribute *attributes, + char *buf, + loff_t pos, + size_t count) +#endif +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct rmi_function_container *fc; + struct rmi_fn_34_data *instance_data; + u16 data_base_addr; + int error; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + data_base_addr = fc->fd.data_base_addr; + + if (count != instance_data->blocksize) { + dev_err(dev, + "%s : Incorrect F34 block size %d. " + "Expected size %d.\n", + __func__, count, instance_data->blocksize); + return -EINVAL; + } + + /* Read the data from flash into buf. The app layer will be blocked + * at reading from the sysfs file. When we return the count (or + * error if we fail) the app will resume. */ + error = rmi_read_block(fc->rmi_dev, data_base_addr + BLK_NUM_OFF, + (unsigned char *)buf, count); + + if (error < 0) { + dev_err(dev, "%s : Could not read data from 0x%04x\n", + __func__, data_base_addr + BLK_NUM_OFF); + return error; + } + + return count; +} + +#ifdef KERNEL_VERSION_ABOVE_2_6_32 +static ssize_t rmi_fn_34_data_write(struct file *data_file, + struct kobject *kobj, + struct bin_attribute *attributes, + char *buf, + loff_t pos, + size_t count) +#else +static ssize_t rmi_fn_34_data_write(struct kobject *kobj, + struct bin_attribute *attributes, + char *buf, + loff_t pos, + size_t count) +#endif +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct rmi_function_container *fc; + struct rmi_fn_34_data *instance_data; + u16 data_base_addr; + int error; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + data_base_addr = fc->fd.data_base_addr; + + /* Write the data from buf to flash. The app layer will be + * blocked at writing to the sysfs file. When we return the + * count (or error if we fail) the app will resume. */ + + if (count != instance_data->blocksize) { + dev_err(dev, + "%s : Incorrect F34 block size %d. " + "Expected size %d.\n", + __func__, count, instance_data->blocksize); + return -EINVAL; + } + + /* Write the data block - only if the count is non-zero */ + if (count) { + error = rmi_write_block(fc->rmi_dev, + data_base_addr + BLK_NUM_OFF, + (unsigned char *)buf, + count); + + if (error < 0) { + dev_err(dev, "%s : Could not write block data " + "to 0x%x\n", __func__, + data_base_addr + BLK_NUM_OFF); + return error; + } + } + + return count; +} + +static int __init rmi_f34_module_init(void) +{ + int error; + + error = rmi_register_function_driver(&function_handler); + if (error < 0) { + pr_err("%s : register failed !\n", __func__); + return error; + } + + return 0; +} + +static void rmi_f34_module_exit(void) +{ + rmi_unregister_function_driver(&function_handler); +} + +module_init(rmi_f34_module_init); +module_exit(rmi_f34_module_exit); + +MODULE_AUTHOR("Eric Andersson "); +MODULE_DESCRIPTION("RMI f34 module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/rmi4/rmi_f54.c b/drivers/input/touchscreen/rmi4/rmi_f54.c new file mode 100644 index 000000000000..11bb0b934bef --- /dev/null +++ b/drivers/input/touchscreen/rmi4/rmi_f54.c @@ -0,0 +1,1347 @@ + +/* + * Copyright (c) 2011 Synaptics Incorporated + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include +#include "rmi_driver.h" + +/* Set this to 1 for raw hex dump of returned data. */ +#define RAW_HEX 0 +/* Set this to 1 for human readable dump of returned data. */ +#define HUMAN_READABLE 0 +/* The watchdog timer can be useful when debugging certain firmware related + * issues. + */ +#define F54_WATCHDOG 1 + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) +#define KERNEL_VERSION_ABOVE_2_6_32 1 +#endif + +/* define fn $54 commands */ +#define GET_REPORT 1 +#define FORCE_CAL 2 + +/* status */ +#define BUSY 1 +#define IDLE 0 + +/* Offsets for data */ +#define RMI_F54_REPORT_DATA_OFFSET 3 +#define RMI_F54_FIFO_OFFSET 1 +#define RMI_F54_NUM_TX_OFFSET 1 +#define RMI_F54_NUM_RX_OFFSET 0 + +/* Fixed sizes of reports */ +#define RMI_54_FULL_RAW_CAP_MIN_MAX_SIZE 4 +#define RMI_54_HIGH_RESISTANCE_SIZE 6 + +/* definitions for F54 Query Registers in ultra-portable unionstruct form */ +struct f54_ad_query { + /* query 0 */ + u8 number_of_receiver_electrodes; + + /* query 1 */ + u8 number_of_transmitter_electrodes; + + union { + struct { + /* query2 */ + u8 f54_ad_query2_b0__1:2; + u8 has_baseline:1; + u8 has_image8:1; + u8 f54_ad_query2_b4__5:2; + u8 has_image16:1; + u8 f54_ad_query2_b7:1; + }; + u8 f54_ad_query2; + }; + + /* query 3.0 and 3.1 */ + u16 clock_rate; + + /* query 4 */ + u8 touch_controller_family; + + /* query 5 */ + union { + struct { + u8 has_pixel_touch_threshold_adjustment:1; + u8 f54_ad_query5_b1__7:7; + }; + u8 f54_ad_query5; + }; + + /* query 6 */ + union { + struct { + u8 has_sensor_assignment:1; + u8 has_interference_metric:1; + u8 has_sense_frequency_control:1; + u8 has_firmware_noise_mitigation:1; + u8 f54_ad_query6_b4:1; + u8 has_two_byte_report_rate:1; + u8 has_one_byte_report_rate:1; + u8 has_relaxation_control:1; + }; + u8 f54_ad_query6; + }; + + /* query 7 */ + union { + struct { + u8 curve_compensation_mode:2; + u8 f54_ad_query7_b2__7:6; + }; + u8 f54_ad_query7; + }; + + /* query 8 */ + union { + struct { + u8 f54_ad_query2_b0:1; + u8 has_iir_filter:1; + u8 has_cmn_removal:1; + u8 has_cmn_maximum:1; + u8 has_pixel_threshold_hysteresis:1; + u8 has_edge_compensation:1; + u8 has_perf_frequency_noisecontrol:1; + u8 f54_ad_query8_b7:1; + }; + u8 f54_ad_query8; + }; + + u8 f54_ad_query9; + u8 f54_ad_query10; + u8 f54_ad_query11; + + /* query 12 */ + union { + struct { + u8 number_of_sensing_frequencies:4; + u8 f54_ad_query12_b4__7:4; + }; + u8 f54_ad_query12; + }; +}; + +/* define report types */ +enum f54_report_types { + /* The numbering should follow automatically, here for clarity */ + F54_8BIT_IMAGE = 1, + F54_16BIT_IMAGE = 2, + F54_RAW_16BIT_IMAGE = 3, + F54_HIGH_RESISTANCE = 4, + F54_TX_TO_TX_SHORT = 5, + F54_RX_TO_RX1 = 7, + F54_TRUE_BASELINE = 9, + F54_FULL_RAW_CAP_MIN_MAX = 13, + F54_RX_OPENS1 = 14, + F54_TX_OPEN = 15, + F54_TX_TO_GROUND = 16, + F54_RX_TO_RX2 = 17, + F54_RX_OPENS2 = 18, + F54_FULL_RAW_CAP = 19, + F54_FULL_RAW_CAP_RX_COUPLING_COMP = 20 +}; + +/* data specific to fn $54 that needs to be kept around */ +struct rmi_fn_54_data { + struct f54_ad_query query; + u8 cmd; + enum f54_report_types report_type; + u16 fifoindex; + signed char status; + bool no_auto_cal; + /* + * May need to do something to make sure this reflects what is currently + * in data. + */ + unsigned int report_size; + unsigned char *report_data; + unsigned int bufsize; + struct mutex data_mutex; + struct lock_class_key data_key; + struct mutex status_mutex; + struct lock_class_key status_key; +#if F54_WATCHDOG + struct hrtimer watchdog; +#endif + struct rmi_function_container *fc; + struct work_struct work; +}; + +/* sysfs functions */ +static ssize_t rmi_fn_54_report_type_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_report_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static ssize_t rmi_fn_54_get_report_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static ssize_t rmi_fn_54_force_cal_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static ssize_t rmi_fn_54_status_show(struct device *dev, + struct device_attribute *attr, char *buf); + +#ifdef KERNEL_VERSION_ABOVE_2_6_32 +static ssize_t rmi_fn_54_data_read(struct file *data_file, struct kobject *kobj, +#else +static ssize_t rmi_fn_54_data_read(struct kobject *kobj, +#endif + struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t rmi_fn_54_num_rx_electrodes_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_num_tx_electrodes_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_has_image16_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_has_image8_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_has_baseline_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_clock_rate_show(struct device *dev, + struct device_attribute *attr, char *buf); + + +static ssize_t rmi_fn_54_touch_controller_family_show(struct device *dev, + struct device_attribute *attr, char *buf); + + +static ssize_t rmi_fn_54_has_pixel_touch_threshold_adjustment_show( + struct device *dev, struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_has_sensor_assignment_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_has_interference_metric_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_has_sense_frequency_control_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_has_firmware_noise_mitigation_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_has_two_byte_report_rate_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_has_one_byte_report_rate_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_has_relaxation_control_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_curve_compensation_mode_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_has_iir_filter_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_has_cmn_removal_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_has_cmn_maximum_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_has_pixel_threshold_hysteresis_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_has_edge_compensation_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_has_perf_frequency_noisecontrol_show( + struct device *dev, struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_number_of_sensing_frequencies_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_no_auto_cal_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_no_auto_cal_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static ssize_t rmi_fn_54_fifoindex_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmi_fn_54_fifoindex_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static struct device_attribute attrs[] = { + __ATTR(report_type, RMI_RW_ATTR, + rmi_fn_54_report_type_show, rmi_fn_54_report_type_store), + __ATTR(get_report, RMI_WO_ATTR, + rmi_show_error, rmi_fn_54_get_report_store), + __ATTR(force_cal, RMI_WO_ATTR, + rmi_show_error, rmi_fn_54_force_cal_store), + __ATTR(status, RMI_RO_ATTR, + rmi_fn_54_status_show, rmi_store_error), + __ATTR(num_rx_electrodes, RMI_RO_ATTR, + rmi_fn_54_num_rx_electrodes_show, rmi_store_error), + __ATTR(num_tx_electrodes, RMI_RO_ATTR, + rmi_fn_54_num_tx_electrodes_show, rmi_store_error), + __ATTR(has_image16, RMI_RO_ATTR, + rmi_fn_54_has_image16_show, rmi_store_error), + __ATTR(has_image8, RMI_RO_ATTR, + rmi_fn_54_has_image8_show, rmi_store_error), + __ATTR(has_baseline, RMI_RO_ATTR, + rmi_fn_54_has_baseline_show, rmi_store_error), + __ATTR(clock_rate, RMI_RO_ATTR, + rmi_fn_54_clock_rate_show, rmi_store_error), + __ATTR(touch_controller_family, RMI_RO_ATTR, + rmi_fn_54_touch_controller_family_show, rmi_store_error), + __ATTR(has_pixel_touch_threshold_adjustment, RMI_RO_ATTR, + rmi_fn_54_has_pixel_touch_threshold_adjustment_show + , rmi_store_error), + __ATTR(has_sensor_assignment, RMI_RO_ATTR, + rmi_fn_54_has_sensor_assignment_show, rmi_store_error), + __ATTR(has_interference_metric, RMI_RO_ATTR, + rmi_fn_54_has_interference_metric_show, rmi_store_error), + __ATTR(has_sense_frequency_control, RMI_RO_ATTR, + rmi_fn_54_has_sense_frequency_control_show, rmi_store_error), + __ATTR(has_firmware_noise_mitigation, RMI_RO_ATTR, + rmi_fn_54_has_firmware_noise_mitigation_show, rmi_store_error), + __ATTR(has_two_byte_report_rate, RMI_RO_ATTR, + rmi_fn_54_has_two_byte_report_rate_show, rmi_store_error), + __ATTR(has_one_byte_report_rate, RMI_RO_ATTR, + rmi_fn_54_has_one_byte_report_rate_show, rmi_store_error), + __ATTR(has_relaxation_control, RMI_RO_ATTR, + rmi_fn_54_has_relaxation_control_show, rmi_store_error), + __ATTR(curve_compensation_mode, RMI_RO_ATTR, + rmi_fn_54_curve_compensation_mode_show, rmi_store_error), + __ATTR(has_iir_filter, RMI_RO_ATTR, + rmi_fn_54_has_iir_filter_show, rmi_store_error), + __ATTR(has_cmn_removal, RMI_RO_ATTR, + rmi_fn_54_has_cmn_removal_show, rmi_store_error), + __ATTR(has_cmn_maximum, RMI_RO_ATTR, + rmi_fn_54_has_cmn_maximum_show, rmi_store_error), + __ATTR(has_pixel_threshold_hysteresis, RMI_RO_ATTR, + rmi_fn_54_has_pixel_threshold_hysteresis_show, rmi_store_error), + __ATTR(has_edge_compensation, RMI_RO_ATTR, + rmi_fn_54_has_edge_compensation_show, rmi_store_error), + __ATTR(has_perf_frequency_noisecontrol, RMI_RO_ATTR, + rmi_fn_54_has_perf_frequency_noisecontrol_show, rmi_store_error), + __ATTR(number_of_sensing_frequencies, RMI_RO_ATTR, + rmi_fn_54_number_of_sensing_frequencies_show, rmi_store_error), + __ATTR(no_auto_cal, RMI_RW_ATTR, + rmi_fn_54_no_auto_cal_show, rmi_fn_54_no_auto_cal_store), + __ATTR(fifoindex, RMI_RW_ATTR, + rmi_fn_54_fifoindex_show, rmi_fn_54_fifoindex_store), +}; + +struct bin_attribute dev_rep_data = { + .attr = { + .name = "rep_data", + .mode = RMI_RO_ATTR}, + .size = 0, + .read = rmi_fn_54_data_read, +}; + +#if F54_WATCHDOG +static enum hrtimer_restart clear_status(struct hrtimer *timer); + +static void clear_status_worker(struct work_struct *work); +#endif + +static int rmi_f54_init(struct rmi_function_container *fc) +{ + struct rmi_fn_54_data *instance_data; + int retval = 0; + int attr_count = 0; + + dev_info(&fc->dev, "Intializing F54."); + + instance_data = kzalloc(sizeof(struct rmi_fn_54_data), GFP_KERNEL); + if (!instance_data) { + dev_err(&fc->dev, "Failed to allocate rmi_fn_54_data.\n"); + retval = -ENOMEM; + goto error_exit; + } + fc->data = instance_data; + instance_data->fc = fc; + +#if F54_WATCHDOG + /* Set up watchdog timer to catch unanswered get_report commands */ + hrtimer_init(&instance_data->watchdog, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + instance_data->watchdog.function = clear_status; + + /* work function to do unlocking */ + INIT_WORK(&instance_data->work, clear_status_worker); +#endif + + /* Read F54 Query Data */ + retval = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr, + (u8 *)&instance_data->query, sizeof(instance_data->query)); + if (retval < 0) { + dev_err(&fc->dev, "Could not read query registers" + " from 0x%04x\n", fc->fd.query_base_addr); + goto error_exit; + } + + __mutex_init(&instance_data->data_mutex, "data_mutex", + &instance_data->data_key); + + __mutex_init(&instance_data->status_mutex, "status_mutex", + &instance_data->status_key); + + dev_dbg(&fc->dev, "Creating sysfs files."); + /* Set up sysfs device attributes. */ + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + if (sysfs_create_file + (&fc->dev.kobj, &attrs[attr_count].attr) < 0) { + dev_err(&fc->dev, "Failed to create sysfs file for %s.", + attrs[attr_count].attr.name); + retval = -ENODEV; + goto error_exit; + } + } + /* Binary sysfs file to report the data back */ + retval = sysfs_create_bin_file(&fc->dev.kobj, &dev_rep_data); + if (retval < 0) { + dev_err(&fc->dev, "Failed to create sysfs file for F54 data " + "(error = %d).\n", retval); + retval = -ENODEV; + goto error_exit; + } + instance_data->status = IDLE; + return retval; + +error_exit: + dev_err(&fc->dev, "An error occured in F54 init!\n"); + for (attr_count--; attr_count >= 0; attr_count--) + sysfs_remove_file(&fc->dev.kobj, + &attrs[attr_count].attr); + kfree(instance_data); + return retval; +} + +static void set_report_size(struct rmi_fn_54_data *data) +{ + u8 rx = data->query.number_of_receiver_electrodes; + u8 tx = data->query.number_of_transmitter_electrodes; + switch (data->report_type) { + case F54_8BIT_IMAGE: + data->report_size = rx * tx; + break; + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_COUPLING_COMP: + data->report_size = 2 * rx * tx; + break; + case F54_HIGH_RESISTANCE: + data->report_size = RMI_54_HIGH_RESISTANCE_SIZE; + break; + case F54_FULL_RAW_CAP_MIN_MAX: + data->report_size = RMI_54_FULL_RAW_CAP_MIN_MAX_SIZE; + break; + case F54_TX_TO_TX_SHORT: + case F54_TX_OPEN: + case F54_TX_TO_GROUND: + data->report_size = (tx + 7) / 8; + break; + case F54_RX_TO_RX1: + case F54_RX_OPENS1: + if (rx < tx) + data->report_size = 2 * rx * rx; + else + data->report_size = 2 * rx * tx; + break; + case F54_RX_TO_RX2: + case F54_RX_OPENS2: + if (rx <= tx) + data->report_size = 0; + else + data->report_size = 2 * rx * (rx - tx); + break; + default: + data->report_size = 0; + } +} + +int rmi_f54_attention(struct rmi_function_container *fc, u8 *irq_bits) +{ + struct rmi_driver *driver = fc->rmi_dev->driver; + char fifo[2]; + struct rmi_fn_54_data *data = fc->data; + int error = 0; + + set_report_size(data); + if (data->report_size == 0) { + dev_err(&fc->dev, "Invalid report type set in %s. " + "This should never happen.\n", __func__); + error = -EINVAL; + goto error_exit; + } + /* + * We need to ensure the buffer is big enough. A Buffer size of 0 means + * that the buffer has not been allocated. + */ + if (data->bufsize < data->report_size) { + mutex_lock(&data->data_mutex); + if (data->bufsize > 0) + kfree(data->report_data); + data->report_data = kzalloc(data->report_size, GFP_KERNEL); + if (!data->report_data) { + dev_err(&fc->dev, "Failed to allocate report_data.\n"); + error = -ENOMEM; + data->bufsize = 0; + mutex_unlock(&data->data_mutex); + goto error_exit; + } + data->bufsize = data->report_size; + mutex_unlock(&data->data_mutex); + } + dev_vdbg(&fc->dev, "F54 Interrupt handler is running.\nSize: %d\n", + data->report_size); + /* + * Read report type, fifo high, and fifo low + * error = rmi_read_multiple(rmifninfo->sensor, + * rmifninfo->function_descriptor.data_base_addr , + * repfifo,3); + */ + /* Write 0 to fifohi and fifolo. */ + fifo[0] = 0; + fifo[1] = 0; + error = rmi_write_block(fc->rmi_dev, fc->fd.data_base_addr + + RMI_F54_FIFO_OFFSET, fifo, sizeof(fifo)); + if (error < 0) + dev_err(&fc->dev, "Failed to write fifo to zero!\n"); + else + error = rmi_read_block(fc->rmi_dev, + fc->fd.data_base_addr + RMI_F54_REPORT_DATA_OFFSET, + data->report_data, data->report_size); + if (error < 0) + dev_err(&fc->dev, "F54 data read failed. Code: %d.\n", error); + else if (error != data->report_size) { + error = -EINVAL; + goto error_exit; + } +#if RAW_HEX + int l; + /* Debugging: Print out the file in hex. */ + pr_info("Report data (raw hex):\n"); + for (l = 0; l < data->report_size; l += 2) { + pr_info("%03d: 0x%02x%02x\n", l/2, + data->report_data[l+1], data->report_data[l]); + } +#endif +#if HUMAN_READABLE + /* Debugging: Print out file in human understandable image */ + switch (data->report_type) { + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_COUPLING_COMP: + pr_info("Report data (Image):\n"); + int i, j, k; + char c[2]; + short s; + k = 0; + for (i = 0; i < data->query.number_of_transmitter_electrodes; + i++) { + for (j = 0; j < + data->query.number_of_receiver_electrodes; j++) { + c[0] = data->report_data[k]; + c[1] = data->report_data[k+1]; + memcpy(&s, &c, 2); + if (s < -64) + printk("."); + else if (s < 0) + printk("-"); + else if (s > 64) + printk("*"); + else if (s > 0) + printk("+"); + else + printk("0"); + k += 2; + } + pr_info("\n"); + } + pr_info("EOF\n"); + break; + default: + pr_info("Report type %d debug image not supported", + data->report_type); + } +#endif + error = IDLE; +error_exit: + mutex_lock(&data->status_mutex); + /* Turn back on other interupts, if it + * appears that we turned them off. */ + if (driver->restore_irq_mask) { + dev_dbg(&fc->dev, "Restoring interupts!\n"); + driver->restore_irq_mask(fc->rmi_dev); + } else { + dev_err(&fc->dev, "No way to restore interrupts!\n"); + } + data->status = error; + mutex_unlock(&data->status_mutex); + return data->status; +} + + +#if F54_WATCHDOG +static void clear_status_worker(struct work_struct *work) +{ + struct rmi_fn_54_data *data = container_of(work, + struct rmi_fn_54_data, work); + struct rmi_function_container *fc = data->fc; + struct rmi_driver *driver = fc->rmi_dev->driver; + char command; + int result; + + mutex_lock(&data->status_mutex); + if (data->status == BUSY) { + pr_info("F54 Timout Occured: Determining status.\n"); + result = rmi_read_block(fc->rmi_dev, fc->fd.command_base_addr, + &command, 1); + if (result < 0) { + dev_err(&fc->dev, "Could not read get_report register " + "from 0x%04x\n", fc->fd.command_base_addr); + data->status = -ETIMEDOUT; + } else { + if (command & GET_REPORT) { + dev_warn(&fc->dev, "Report type unsupported!"); + data->status = -EINVAL; + } else { + data->status = -ETIMEDOUT; + } + } + if (driver->restore_irq_mask) { + dev_dbg(&fc->dev, "Restoring interupts!\n"); + driver->restore_irq_mask(fc->rmi_dev); + } else { + dev_err(&fc->dev, "No way to restore interrupts!\n"); + } + } + mutex_unlock(&data->status_mutex); +} + +static enum hrtimer_restart clear_status(struct hrtimer *timer) +{ + struct rmi_fn_54_data *data = container_of(timer, + struct rmi_fn_54_data, watchdog); + schedule_work(&(data->work)); + return HRTIMER_NORESTART; +} +#endif + +/* Check if report_type is valid */ +static bool is_report_type_valid(enum f54_report_types reptype) +{ + /* Basic checks on report_type to ensure we write a valid type + * to the sensor. + * TODO: Check Query3 to see if some specific reports are + * available. This is currently listed as a reserved register. + */ + switch (reptype) { + case F54_8BIT_IMAGE: + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_HIGH_RESISTANCE: + case F54_TX_TO_TX_SHORT: + case F54_RX_TO_RX1: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP_MIN_MAX: + case F54_RX_OPENS1: + case F54_TX_OPEN: + case F54_TX_TO_GROUND: + case F54_RX_TO_RX2: + case F54_RX_OPENS2: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_COUPLING_COMP: + return true; + break; + default: + return false; + } +} + +/* SYSFS file show/store functions */ +static ssize_t rmi_fn_54_report_type_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *instance_data; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->report_type); +} + +static ssize_t rmi_fn_54_report_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { + int result; + unsigned long val; + unsigned char data; + struct rmi_function_container *fc; + struct rmi_fn_54_data *instance_data; + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + /* need to convert the string data to an actual value */ + result = strict_strtoul(buf, 10, &val); + if (result) + return result; + if (!is_report_type_valid(val)) { + dev_err(dev, "%s : Report type %d is invalid.\n", + __func__, (u8) val); + return -EINVAL; + } + mutex_lock(&instance_data->status_mutex); + if (instance_data->status != BUSY) { + instance_data->report_type = (enum f54_report_types)val; + data = (char)val; + /* Write the Report Type back to the first Block + * Data registers (F54_AD_Data0). */ + result = + rmi_write_block(fc->rmi_dev, fc->fd.data_base_addr, + &data, 1); + mutex_unlock(&instance_data->status_mutex); + if (result < 0) { + dev_err(dev, "%s : Could not write report type to" + " 0x%x\n", __func__, fc->fd.data_base_addr); + return result; + } + return count; + } else { + dev_err(dev, "%s : Report type cannot be changed in the middle" + " of command.\n", __func__); + mutex_unlock(&instance_data->status_mutex); + return -EINVAL; + } +} + +static ssize_t rmi_fn_54_get_report_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { + unsigned long val; + int error, result; + struct rmi_function_container *fc; + struct rmi_fn_54_data *instance_data; + struct rmi_driver *driver; + u8 command; + fc = to_rmi_function_container(dev); + instance_data = fc->data; + driver = fc->rmi_dev->driver; + + /* need to convert the string data to an actual value */ + error = strict_strtoul(buf, 10, &val); + if (error) + return error; + /* Do nothing if not set to 1. This prevents accidental commands. */ + if (val != 1) + return count; + command = (unsigned char)GET_REPORT; + /* Basic checks on report_type to ensure we write a valid type + * to the sensor. + * TODO: Check Query3 to see if some specific reports are + * available. This is currently listed as a reserved register. + */ + if (!is_report_type_valid(instance_data->report_type)) { + dev_err(dev, "%s : Report type %d is invalid.\n", + __func__, instance_data->report_type); + return -EINVAL; + } + mutex_lock(&instance_data->status_mutex); + if (instance_data->status != IDLE) { + if (instance_data->status != BUSY) { + dev_err(dev, "F54 status is in an abnormal state: 0x%x", + instance_data->status); + } + mutex_unlock(&instance_data->status_mutex); + return count; + } + /* Store interrupts */ + /* Do not exit if we fail to turn off interupts. We are likely + * to still get useful data. The report data can, however, be + * corrupted, and there may be unexpected behavior. + */ + dev_dbg(dev, "Storing and overriding interupts\n"); + if (driver->store_irq_mask) + driver->store_irq_mask(fc->rmi_dev, + fc->irq_mask); + else + dev_err(dev, "No way to store interupts!\n"); + instance_data->status = BUSY; + + /* small delay to avoid race condition in firmare. This value is a bit + * higher than absolutely necessary. Should be removed once issue is + * resolved in firmware. */ + + mdelay(2); + + /* Write the command to the command register */ + result = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr, + &command, 1); + mutex_unlock(&instance_data->status_mutex); + if (result < 0) { + dev_err(dev, "%s : Could not write command to 0x%x\n", + __func__, fc->fd.command_base_addr); + return result; + } +#if F54_WATCHDOG + /* start watchdog timer */ + hrtimer_start(&instance_data->watchdog, ktime_set(1, 0), + HRTIMER_MODE_REL); +#endif + return count; +} + +static ssize_t rmi_fn_54_force_cal_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { + unsigned long val; + int error, result; + struct rmi_function_container *fc; + struct rmi_fn_54_data *instance_data; + struct rmi_driver *driver; + u8 command; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + driver = fc->rmi_dev->driver; + + /* need to convert the string data to an actual value */ + error = strict_strtoul(buf, 10, &val); + if (error) + return error; + /* Do nothing if not set to 1. This prevents accidental commands. */ + if (val != 1) + return count; + + command = (unsigned char)FORCE_CAL; + + if (instance_data->status == BUSY) + return -EBUSY; + /* Write the command to the command register */ + result = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr, + &command, 1); + if (result < 0) { + dev_err(dev, "%s : Could not write command to 0x%x\n", + __func__, fc->fd.command_base_addr); + return result; + } + return count; +} + +static ssize_t rmi_fn_54_status_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *instance_data; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%d\n", instance_data->status); +} + +static ssize_t rmi_fn_54_num_rx_electrodes_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.number_of_receiver_electrodes); +} + +static ssize_t rmi_fn_54_num_tx_electrodes_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.number_of_transmitter_electrodes); +} + +static ssize_t rmi_fn_54_has_image16_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.has_image16); +} + +static ssize_t rmi_fn_54_has_image8_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.has_image8); +} + +static ssize_t rmi_fn_54_has_baseline_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.has_baseline); +} + +static ssize_t rmi_fn_54_clock_rate_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.clock_rate); +} + + +static ssize_t rmi_fn_54_touch_controller_family_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.touch_controller_family); +} + + +static ssize_t rmi_fn_54_has_pixel_touch_threshold_adjustment_show( + struct device *dev, struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.has_pixel_touch_threshold_adjustment); +} + +static ssize_t rmi_fn_54_has_sensor_assignment_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.has_sensor_assignment); +} + +static ssize_t rmi_fn_54_has_interference_metric_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.has_interference_metric); +} + +static ssize_t rmi_fn_54_has_sense_frequency_control_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.has_sense_frequency_control); +} + +static ssize_t rmi_fn_54_has_firmware_noise_mitigation_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.has_firmware_noise_mitigation); +} + +static ssize_t rmi_fn_54_has_two_byte_report_rate_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.has_two_byte_report_rate); +} + +static ssize_t rmi_fn_54_has_one_byte_report_rate_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.has_one_byte_report_rate); +} + +static ssize_t rmi_fn_54_has_relaxation_control_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.has_relaxation_control); +} + +static ssize_t rmi_fn_54_curve_compensation_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.curve_compensation_mode); +} + +static ssize_t rmi_fn_54_has_iir_filter_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.has_iir_filter); +} + +static ssize_t rmi_fn_54_has_cmn_removal_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.has_cmn_removal); +} + +static ssize_t rmi_fn_54_has_cmn_maximum_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.has_cmn_maximum); +} + +static ssize_t rmi_fn_54_has_pixel_threshold_hysteresis_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.has_pixel_threshold_hysteresis); +} + +static ssize_t rmi_fn_54_has_edge_compensation_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.has_edge_compensation); +} + +static ssize_t rmi_fn_54_has_perf_frequency_noisecontrol_show( + struct device *dev, struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.has_perf_frequency_noisecontrol); +} + +static ssize_t rmi_fn_54_number_of_sensing_frequencies_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->query.number_of_sensing_frequencies); +} + + +static ssize_t rmi_fn_54_no_auto_cal_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *instance_data; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + instance_data->no_auto_cal ? 1 : 0); +} + +static ssize_t rmi_fn_54_no_auto_cal_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { + int result; + unsigned long val; + unsigned char data; + struct rmi_function_container *fc; + struct rmi_fn_54_data *instance_data; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + /* need to convert the string data to an actual value */ + result = strict_strtoul(buf, 10, &val); + + /* if an error occured, return it */ + if (result) + return result; + /* Do nothing if not 0 or 1. This prevents accidental commands. */ + if (val > 1) + return count; + /* Read current control values */ + result = + rmi_read_block(fc->rmi_dev, fc->fd.control_base_addr, &data, 1); + + /* if the current control registers are already set as we want them, do + * nothing to them */ + if ((data & 1) == val) + return count; + /* Write the control back to the control register (F54_AD_Ctrl0) + * Ignores everything but bit 0 */ + data = (data & ~1) | (val & 0x01); /* bit mask for lowest bit */ + result = + rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr, &data, 1); + if (result < 0) { + dev_err(dev, "%s : Could not write control to 0x%x\n", + __func__, fc->fd.control_base_addr); + return result; + } + /* update our internal representation iff the write succeeds */ + instance_data->no_auto_cal = (val == 1); + return count; +} + +static ssize_t rmi_fn_54_fifoindex_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *instance_data; + struct rmi_driver *driver; + unsigned char temp_buf[2]; + int retval; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + driver = fc->rmi_dev->driver; + + /* Read fifoindex from device */ + retval = rmi_read_block(fc->rmi_dev, + fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET, + temp_buf, ARRAY_SIZE(temp_buf)); + + if (retval < 0) { + dev_err(dev, "Could not read fifoindex from 0x%04x\n", + fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET); + return retval; + } + batohs(&instance_data->fifoindex, temp_buf); + return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->fifoindex); +} +static ssize_t rmi_fn_54_fifoindex_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int error; + unsigned long val; + unsigned char data[2]; + struct rmi_function_container *fc; + struct rmi_fn_54_data *instance_data; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + /* need to convert the string data to an actual value */ + error = strict_strtoul(buf, 10, &val); + + if (error) + return error; + + instance_data->fifoindex = val; + + /* Write the FifoIndex back to the first data registers. */ + hstoba(data, (unsigned short)val); + + error = rmi_write_block(fc->rmi_dev, + fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET, + data, + ARRAY_SIZE(data)); + + if (error < 0) { + dev_err(dev, "%s : Could not write fifoindex to 0x%x\n", + __func__, fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET); + return error; + } + return count; +} + +/* Provide access to last report */ +#ifdef KERNEL_VERSION_ABOVE_2_6_32 +static ssize_t rmi_fn_54_data_read(struct file *data_file, struct kobject *kobj, +#else +static ssize_t rmi_fn_54_data_read(struct kobject *kobj, +#endif + struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + struct device *dev; + struct rmi_function_container *fc; + struct rmi_fn_54_data *instance_data; + + dev = container_of(kobj, struct device, kobj); + fc = to_rmi_function_container(dev); + instance_data = fc->data; + mutex_lock(&instance_data->data_mutex); + if (count < instance_data->report_size) { + dev_err(dev, + "%s: F54 report size too large for buffer: %d." + " Need at least: %d for Report type: %d.\n", + __func__, count, instance_data->report_size, + instance_data->report_type); + mutex_unlock(&instance_data->data_mutex); + return -EINVAL; + } + if (instance_data->report_data) { + /* Copy data from instance_data to buffer */ + memcpy(buf, instance_data->report_data, + instance_data->report_size); + mutex_unlock(&instance_data->data_mutex); + dev_dbg(dev, "%s: Presumably successful.", __func__); + return instance_data->report_size; + } else { + dev_err(dev, "%s: F54 report_data does not exist!\n", __func__); + mutex_unlock(&instance_data->data_mutex); + return -EINVAL; + } +} + +static struct rmi_function_handler function_handler = { + .func = 0x54, + .init = rmi_f54_init, + .attention = rmi_f54_attention +}; + +static int __init rmi_f54_module_init(void) +{ + int error; + + error = rmi_register_function_driver(&function_handler); + if (error < 0) { + pr_err("%s: register failed!\n", __func__); + return error; + } + return 0; +} + +static void rmi_f54_module_exit(void) +{ + rmi_unregister_function_driver(&function_handler); +} + +module_init(rmi_f54_module_init); +module_exit(rmi_f54_module_exit); + +MODULE_AUTHOR("Daniel Rosenberg "); +MODULE_DESCRIPTION("RMI F54 module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/rmi4/rmi_i2c.c b/drivers/input/touchscreen/rmi4/rmi_i2c.c new file mode 100644 index 000000000000..9f3ecf8eb97d --- /dev/null +++ b/drivers/input/touchscreen/rmi4/rmi_i2c.c @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2011 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define COMMS_DEBUG 0 + +#define IRQ_DEBUG 0 + +#define RMI_PAGE_SELECT_REGISTER 0xff +#define RMI_I2C_PAGE(addr) (((addr) >> 8) & 0xff) + +static char *phys_proto_name = "i2c"; + +struct rmi_i2c_data { + struct mutex page_mutex; + int page; + int enabled; + int irq; + int irq_flags; + struct rmi_phys_device *phys; +}; + +static irqreturn_t rmi_i2c_irq_thread(int irq, void *p) +{ + struct rmi_phys_device *phys = p; + struct rmi_device *rmi_dev = phys->rmi_dev; + struct rmi_driver *driver = rmi_dev->driver; + struct rmi_device_platform_data *pdata = phys->dev->platform_data; + +#if IRQ_DEBUG + dev_dbg(phys->dev, "ATTN gpio, value: %d.\n", + gpio_get_value(irq_to_gpio(irq))); +#endif + if (gpio_get_value(irq_to_gpio(irq)) == pdata->irq_polarity) { + phys->info.attn_count++; + if (driver && driver->irq_handler && rmi_dev) + driver->irq_handler(rmi_dev, irq); + } + + return IRQ_HANDLED; +} + +/* + * rmi_set_page - Set RMI page + * @phys: The pointer to the rmi_phys_device struct + * @page: The new page address. + * + * RMI devices have 16-bit addressing, but some of the physical + * implementations (like SMBus) only have 8-bit addressing. So RMI implements + * a page address at 0xff of every page so we can reliable page addresses + * every 256 registers. + * + * The page_mutex lock must be held when this function is entered. + * + * Returns zero on success, non-zero on failure. + */ +static int rmi_set_page(struct rmi_phys_device *phys, unsigned int page) +{ + struct i2c_client *client = to_i2c_client(phys->dev); + struct rmi_i2c_data *data = phys->data; + char txbuf[2] = {RMI_PAGE_SELECT_REGISTER, page}; + int retval; + +#if COMMS_DEBUG + dev_dbg(&client->dev, "RMI4 I2C writes 3 bytes: %02x %02x\n", + txbuf[0], txbuf[1]); +#endif + phys->info.tx_count++; + phys->info.tx_bytes += sizeof(txbuf); + retval = i2c_master_send(client, txbuf, sizeof(txbuf)); + if (retval != sizeof(txbuf)) { + phys->info.tx_errs++; + dev_err(&client->dev, + "%s: set page failed: %d.", __func__, retval); + return (retval < 0) ? retval : -EIO; + } + data->page = page; + return 0; +} + +static int rmi_i2c_write_block(struct rmi_phys_device *phys, u16 addr, u8 *buf, + int len) +{ + struct i2c_client *client = to_i2c_client(phys->dev); + struct rmi_i2c_data *data = phys->data; + u8 txbuf[len + 1]; + int retval; +#if COMMS_DEBUG + int i; +#endif + + txbuf[0] = addr & 0xff; + memcpy(txbuf + 1, buf, len); + + mutex_lock(&data->page_mutex); + + if (RMI_I2C_PAGE(addr) != data->page) { + retval = rmi_set_page(phys, RMI_I2C_PAGE(addr)); + if (retval < 0) + goto exit; + } + +#if COMMS_DEBUG + dev_dbg(&client->dev, "RMI4 I2C writes %d bytes: ", sizeof(txbuf)); + for (i = 0; i < sizeof(txbuf); i++) + dev_dbg(&client->dev, "%02x ", txbuf[i]); + dev_dbg(&client->dev, "\n"); +#endif + + phys->info.tx_count++; + phys->info.tx_bytes += sizeof(txbuf); + retval = i2c_master_send(client, txbuf, sizeof(txbuf)); + if (retval < 0) + phys->info.tx_errs++; + +exit: + mutex_unlock(&data->page_mutex); + return retval; +} + +static int rmi_i2c_write(struct rmi_phys_device *phys, u16 addr, u8 data) +{ + int retval = rmi_i2c_write_block(phys, addr, &data, 1); + return (retval < 0) ? retval : 0; +} + +static int rmi_i2c_read_block(struct rmi_phys_device *phys, u16 addr, u8 *buf, + int len) +{ + struct i2c_client *client = to_i2c_client(phys->dev); + struct rmi_i2c_data *data = phys->data; + u8 txbuf[1] = {addr & 0xff}; + int retval; +#if COMMS_DEBUG + int i; +#endif + + mutex_lock(&data->page_mutex); + + if (RMI_I2C_PAGE(addr) != data->page) { + retval = rmi_set_page(phys, RMI_I2C_PAGE(addr)); + if (retval < 0) + goto exit; + } + +#if COMMS_DEBUG + dev_dbg(&client->dev, "RMI4 I2C writes 1 bytes: %02x\n", txbuf[0]); +#endif + phys->info.tx_count++; + phys->info.tx_bytes += sizeof(txbuf); + retval = i2c_master_send(client, txbuf, sizeof(txbuf)); + if (retval != sizeof(txbuf)) { + phys->info.tx_errs++; + retval = (retval < 0) ? retval : -EIO; + goto exit; + } + + retval = i2c_master_recv(client, buf, len); + + phys->info.rx_count++; + phys->info.rx_bytes += len; + if (retval < 0) + phys->info.rx_errs++; +#if COMMS_DEBUG + else { + dev_dbg(&client->dev, "RMI4 I2C received %d bytes: ", len); + for (i = 0; i < len; i++) + dev_dbg(&client->dev, "%02x ", buf[i]); + dev_dbg(&client->dev, "\n"); + } +#endif + +exit: + mutex_unlock(&data->page_mutex); + return retval; +} + +static int rmi_i2c_read(struct rmi_phys_device *phys, u16 addr, u8 *buf) +{ + int retval = rmi_i2c_read_block(phys, addr, buf, 1); + return (retval < 0) ? retval : 0; +} + + +static int acquire_attn_irq(struct rmi_i2c_data *data) +{ + return request_threaded_irq(data->irq, NULL, rmi_i2c_irq_thread, + data->irq_flags, dev_name(data->phys->dev), data->phys); +} + +static int enable_device(struct rmi_phys_device *phys) +{ + int retval = 0; + + struct rmi_i2c_data *data = phys->data; + + if (data->enabled) + return 0; + + retval = acquire_attn_irq(data); + if (retval) + goto error_exit; + + data->enabled = true; + dev_dbg(phys->dev, "Physical device enabled.\n"); + return 0; + +error_exit: + dev_err(phys->dev, "Failed to enable physical device. Code=%d.\n", + retval); + return retval; +} + + +static void disable_device(struct rmi_phys_device *phys) +{ + struct rmi_i2c_data *data = phys->data; + + if (!data->enabled) + return; + + disable_irq(data->irq); + free_irq(data->irq, data->phys); + + dev_dbg(phys->dev, "Physical device disabled.\n"); + data->enabled = false; +} + + +static int rmi_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct rmi_phys_device *rmi_phys; + struct rmi_i2c_data *data; + struct rmi_device_platform_data *pdata = client->dev.platform_data; + int error; + + if (!pdata) { + dev_err(&client->dev, "no platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "i2c_check_functionality error\n"); + return -EIO; + } + + rmi_phys = kzalloc(sizeof(struct rmi_phys_device), GFP_KERNEL); + if (!rmi_phys) + return -ENOMEM; + + data = kzalloc(sizeof(struct rmi_i2c_data), GFP_KERNEL); + if (!data) { + error = -ENOMEM; + goto err_phys; + } + + data->enabled = true; /* We plan to come up enabled. */ + data->irq = gpio_to_irq(pdata->irq); + data->irq_flags = (pdata->irq_polarity == RMI_IRQ_ACTIVE_HIGH) ? + IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; + data->phys = rmi_phys; + + rmi_phys->data = data; + rmi_phys->dev = &client->dev; + + rmi_phys->write = rmi_i2c_write; + rmi_phys->write_block = rmi_i2c_write_block; + rmi_phys->read = rmi_i2c_read; + rmi_phys->read_block = rmi_i2c_read_block; + rmi_phys->enable_device = enable_device; + rmi_phys->disable_device = disable_device; + + rmi_phys->info.proto = phys_proto_name; + + mutex_init(&data->page_mutex); + + /* Setting the page to zero will (a) make sure the PSR is in a + * known state, and (b) make sure we can talk to the device. + */ + error = rmi_set_page(rmi_phys, 0); + if (error) { + dev_err(&client->dev, "Failed to set page select to 0.\n"); + goto err_data; + } + + if (pdata->gpio_config) { + error = pdata->gpio_config(&client->dev, true); + if (error < 0) { + dev_err(&client->dev, "failed to setup irq %d\n", + pdata->irq); + goto err_data; + } + } + + error = rmi_register_phys_device(rmi_phys); + if (error) { + dev_err(&client->dev, + "failed to register physical driver at 0x%.2X.\n", + client->addr); + goto err_data; + } + i2c_set_clientdata(client, rmi_phys); + + if (pdata->irq > 0) { + error = acquire_attn_irq(data); + if (error < 0) { + dev_err(&client->dev, + "request_threaded_irq failed %d\n", + pdata->irq); + goto err_unregister; + } + } + +#if defined(CONFIG_RMI4_DEV) + error = gpio_export(pdata->irq, false); + if (error) { + dev_warn(&client->dev, "%s: WARNING: Failed to " + "export ATTN gpio!\n", __func__); + error = 0; + } else { + error = gpio_export_link(&(rmi_phys->rmi_dev->dev), "attn", + pdata->irq); + if (error) { + dev_warn(&(rmi_phys->rmi_dev->dev), "%s: WARNING: " + "Failed to symlink ATTN gpio!\n", __func__); + error = 0; + } else { + dev_info(&(rmi_phys->rmi_dev->dev), + "%s: Exported GPIO %d.", __func__, pdata->irq); + } + } +#endif /* CONFIG_RMI4_DEV */ + + dev_info(&client->dev, "registered rmi i2c driver at 0x%.2X.\n", + client->addr); + return 0; + +err_unregister: + rmi_unregister_phys_device(rmi_phys); +err_data: + kfree(data); +err_phys: + kfree(rmi_phys); + return error; +} + +static int rmi_i2c_remove(struct i2c_client *client) +{ + struct rmi_phys_device *phys = i2c_get_clientdata(client); + struct rmi_device_platform_data *pd = client->dev.platform_data; + + rmi_unregister_phys_device(phys); + kfree(phys->data); + kfree(phys); + + if (pd->gpio_config) + pd->gpio_config(&client->dev, false); + + return 0; +} + +static const struct i2c_device_id rmi_id[] = { + { "rmi", 0 }, + { "rmi-i2c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rmi_id); + +static struct i2c_driver rmi_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "rmi-i2c" + }, + .id_table = rmi_id, + .probe = rmi_i2c_probe, + .remove = rmi_i2c_remove, +}; + +static int __init rmi_i2c_init(void) +{ + return i2c_add_driver(&rmi_i2c_driver); +} + +static void __exit rmi_i2c_exit(void) +{ + i2c_del_driver(&rmi_i2c_driver); +} + +MODULE_AUTHOR("Christopher Heiny "); +MODULE_AUTHOR("Eric Andersson "); +MODULE_DESCRIPTION("RMI i2c driver"); +MODULE_LICENSE("GPL"); + +module_init(rmi_i2c_init); +module_exit(rmi_i2c_exit); diff --git a/drivers/input/touchscreen/rmi4/rmi_spi.c b/drivers/input/touchscreen/rmi4/rmi_spi.c new file mode 100644 index 000000000000..f675bc0371e9 --- /dev/null +++ b/drivers/input/touchscreen/rmi4/rmi_spi.c @@ -0,0 +1,869 @@ +/* + * Copyright (c) 2011 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define COMMS_DEBUG 0 + +#define RMI_PROTOCOL_VERSION_ADDRESS 0xa0fd +#define SPI_V2_UNIFIED_READ 0xc0 +#define SPI_V2_WRITE 0x40 +#define SPI_V2_PREPARE_SPLIT_READ 0xc8 +#define SPI_V2_EXECUTE_SPLIT_READ 0xca + +#define RMI_SPI_BLOCK_DELAY_US 65 +#define RMI_SPI_BYTE_DELAY_US 65 +#define RMI_SPI_WRITE_DELAY_US 0 + +#define RMI_V1_READ_FLAG 0x80 + +#define RMI_PAGE_SELECT_REGISTER 0x00FF +#define RMI_SPI_PAGE(addr) (((addr) >> 8) & 0x80) + +static char *spi_v1_proto_name = "spi"; +static char *spi_v2_proto_name = "spiv2"; + +struct rmi_spi_data { + struct mutex page_mutex; + int page; + int (*set_page) (struct rmi_phys_device *phys, u8 page); + bool split_read_pending; + int enabled; + int irq; + int irq_flags; + struct rmi_phys_device *phys; + struct completion irq_comp; +}; + +static irqreturn_t rmi_spi_hard_irq(int irq, void *p) +{ + struct rmi_phys_device *phys = p; + struct rmi_spi_data *data = phys->data; + struct rmi_device_platform_data *pdata = phys->dev->platform_data; + + if (data->split_read_pending && + gpio_get_value(irq_to_gpio(irq)) == pdata->irq_polarity) { + phys->info.attn_count++; + complete(&data->irq_comp); + return IRQ_HANDLED; + } + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t rmi_spi_irq_thread(int irq, void *p) +{ + struct rmi_phys_device *phys = p; + struct rmi_device *rmi_dev = phys->rmi_dev; + struct rmi_driver *driver = rmi_dev->driver; + struct rmi_device_platform_data *pdata = phys->dev->platform_data; + + if (gpio_get_value(irq_to_gpio(irq)) == pdata->irq_polarity) { + phys->info.attn_count++; + if (driver && driver->irq_handler) + driver->irq_handler(rmi_dev, irq); + } + + return IRQ_HANDLED; +} + +static int rmi_spi_xfer(struct rmi_phys_device *phys, + const u8 *txbuf, unsigned n_tx, u8 *rxbuf, unsigned n_rx) +{ + struct spi_device *client = to_spi_device(phys->dev); + struct rmi_spi_data *v2_data = phys->data; + struct rmi_device_platform_data *pdata = phys->dev->platform_data; + int status; + struct spi_message message; + struct spi_transfer *xfers; + int total_bytes = n_tx + n_rx; + u8 local_buf[total_bytes]; + int xfer_count = 0; + int xfer_index = 0; + int block_delay = n_rx > 0 ? pdata->spi_data.block_delay_us : 0; + int byte_delay = n_rx > 1 ? pdata->spi_data.read_delay_us : 0; + int write_delay = n_tx > 1 ? pdata->spi_data.write_delay_us : 0; +#if COMMS_DEBUG + int i; +#endif + // pr_info("in function ____%s____ \n", __func__); + + if (v2_data->split_read_pending) { + block_delay = + n_rx > 0 ? pdata->spi_data.split_read_block_delay_us : 0; + byte_delay = + n_tx > 1 ? pdata->spi_data.split_read_byte_delay_us : 0; + write_delay = 0; + } + + if (n_tx) { + phys->info.tx_count++; + phys->info.tx_bytes += n_tx; + if (write_delay) + xfer_count += n_tx; + else + xfer_count += 1; + } + + if (n_rx) { + phys->info.rx_count++; + phys->info.rx_bytes += n_rx; + if (byte_delay) + xfer_count += n_rx; + else + xfer_count += 1; + } + + xfers = kcalloc(xfer_count, + sizeof(struct spi_transfer), GFP_KERNEL); + if (!xfers) + return -ENOMEM; + + spi_message_init(&message); + + if (n_tx) { + if (write_delay) { + for (xfer_index = 0; xfer_index < n_tx; + xfer_index++) { + memset(&xfers[xfer_index], 0, + sizeof(struct spi_transfer)); + xfers[xfer_index].len = 1; + xfers[xfer_index].delay_usecs = write_delay; + xfers[xfer_index].cs_change = 1; + xfers[xfer_index].tx_buf = txbuf + xfer_index; + spi_message_add_tail(&xfers[xfer_index], + &message); + } + } else { + memset(&xfers[0], 0, sizeof(struct spi_transfer)); + xfers[0].len = n_tx; + spi_message_add_tail(&xfers[0], &message); + memcpy(local_buf, txbuf, n_tx); + xfers[0].tx_buf = local_buf; + xfer_index++; + } + if (block_delay){ + xfers[xfer_index-1].delay_usecs = block_delay; + xfers[xfer_index].cs_change = 1; + } + } + if (n_rx) { + if (byte_delay) { + int buffer_offset = n_tx; + for (; xfer_index < xfer_count; xfer_index++) { + memset(&xfers[xfer_index], 0, + sizeof(struct spi_transfer)); + xfers[xfer_index].len = 1; + xfers[xfer_index].delay_usecs = byte_delay; + xfers[xfer_index].cs_change = 1; + xfers[xfer_index].rx_buf = + local_buf + buffer_offset; + buffer_offset++; + spi_message_add_tail(&xfers[xfer_index], + &message); + } + } else { + memset(&xfers[xfer_index], 0, + sizeof(struct spi_transfer)); + xfers[xfer_index].len = n_rx; + xfers[xfer_index].rx_buf = local_buf + n_tx; + spi_message_add_tail(&xfers[xfer_index], &message); + xfer_index++; + } + } + +#if COMMS_DEBUG + if (n_tx) { + dev_info(&client->dev, "SPI sends %d bytes: ", n_tx); + for (i = 0; i < n_tx; i++) + // dev_info(&client->dev, "%02X ", txbuf[i]); + pr_info(": %02X ", txbuf[i]); + // dev_info(&client->dev, "\n"); + pr_info("\n"); + } +#endif + + /* do the i/o */ + if (pdata->spi_data.cs_assert) { + status = pdata->spi_data.cs_assert( + pdata->spi_data.cs_assert_data, true); + if (!status) { + dev_err(phys->dev, "Failed to assert CS."); + /* nonzero means error */ + status = -1; + goto error_exit; + } else + status = 0; + } + + if (pdata->spi_data.pre_delay_us) + udelay(pdata->spi_data.pre_delay_us); + + status = spi_sync(client, &message); + + if (pdata->spi_data.post_delay_us) + udelay(pdata->spi_data.post_delay_us); + + if (pdata->spi_data.cs_assert) { + status = pdata->spi_data.cs_assert( + pdata->spi_data.cs_assert_data, false); + if (!status) { + dev_err(phys->dev, "Failed to deassert CS."); + /* nonzero means error */ + status = -1; + goto error_exit; + } else + status = 0; + } + + if (status == 0) { + memcpy(rxbuf, local_buf + n_tx, n_rx); + status = message.status; + } else { + phys->info.tx_errs++; + phys->info.rx_errs++; + dev_err(phys->dev, "spi_sync failed with error code %d.", + status); + } + +#if COMMS_DEBUG + if (n_rx) { + dev_info(&client->dev, "SPI received %d bytes: ", n_rx); + for (i = 0; i < n_rx; i++) + // dev_info(&client->dev, "%02X ", rxbuf[i]); + pr_info(": %02X ", rxbuf[i]); + // dev_info(&client->dev, "\n"); + pr_info("\n"); + } +#endif + +error_exit: + kfree(xfers); + return status; +} + +static int rmi_spi_v2_write_block(struct rmi_phys_device *phys, u16 addr, + u8 *buf, int len) +{ + struct rmi_spi_data *data = phys->data; + u8 txbuf[len + 4]; + int error; + // pr_info("in function ____%s____ \n", __func__); + + txbuf[0] = SPI_V2_WRITE; + txbuf[1] = (addr >> 8) & 0x00FF; + txbuf[2] = addr & 0x00FF; + txbuf[3] = len; + + memcpy(&txbuf[4], buf, len); + + mutex_lock(&data->page_mutex); + + if (RMI_SPI_PAGE(addr) != data->page) { + error = data->set_page(phys, RMI_SPI_PAGE(addr)); + if (error < 0) + goto exit; + } + + error = rmi_spi_xfer(phys, buf, len + 4, NULL, 0); + if (error < 0) + goto exit; + error = len; + +exit: + mutex_unlock(&data->page_mutex); + return error; +} + +static int rmi_spi_v2_write(struct rmi_phys_device *phys, u16 addr, u8 data) +{ + int error = rmi_spi_v2_write_block(phys, addr, &data, 1); + // pr_info("in function ____%s____ \n", __func__); + + return (error == 1) ? 0 : error; +} + +static int rmi_spi_v1_write_block(struct rmi_phys_device *phys, u16 addr, + u8 *buf, int len) +{ + struct rmi_spi_data *data = phys->data; + unsigned char txbuf[len + 2]; + int error; + // pr_info("in function ____%s____ \n", __func__); + + txbuf[0] = addr >> 8; + txbuf[1] = addr; + memcpy(txbuf+2, buf, len); + + mutex_lock(&data->page_mutex); + + if (RMI_SPI_PAGE(addr) != data->page) { + error = data->set_page(phys, RMI_SPI_PAGE(addr)); + if (error < 0) + goto exit; + } + + error = rmi_spi_xfer(phys, txbuf, len + 2, NULL, 0); + if (error < 0) + goto exit; + error = len; + +exit: + mutex_unlock(&data->page_mutex); + return error; +} + +static int rmi_spi_v1_write(struct rmi_phys_device *phys, u16 addr, u8 data) +{ + int error = rmi_spi_v1_write_block(phys, addr, &data, 1); + // pr_info("in function ____%s____ \n", __func__); + + return (error == 1) ? 0 : error; +} + +static int rmi_spi_v2_split_read_block(struct rmi_phys_device *phys, u16 addr, + u8 *buf, int len) +{ + struct rmi_spi_data *data = phys->data; + u8 txbuf[4]; + u8 rxbuf[len + 1]; /* one extra byte for read length */ + int error; + // pr_info("in function ____%s____ \n", __func__); + + txbuf[0] = SPI_V2_PREPARE_SPLIT_READ; + txbuf[1] = (addr >> 8) & 0x00FF; + txbuf[2] = addr & 0x00ff; + txbuf[3] = len; + + mutex_lock(&data->page_mutex); + + if (RMI_SPI_PAGE(addr) != data->page) { + error = data->set_page(phys, RMI_SPI_PAGE(addr)); + if (error < 0) + goto exit; + } + + data->split_read_pending = true; + + error = rmi_spi_xfer(phys, txbuf, 4, NULL, 0); + if (error < 0) { + data->split_read_pending = false; + goto exit; + } + + wait_for_completion(&data->irq_comp); + + txbuf[0] = SPI_V2_EXECUTE_SPLIT_READ; + txbuf[1] = 0; + + error = rmi_spi_xfer(phys, txbuf, 2, rxbuf, len + 1); + data->split_read_pending = false; + if (error < 0) + goto exit; + + /* first byte is length */ + if (rxbuf[0] != len) { + error = -EIO; + goto exit; + } + + memcpy(buf, rxbuf + 1, len); + error = len; + +exit: + mutex_unlock(&data->page_mutex); + return error; +} + +static int rmi_spi_v2_read_block(struct rmi_phys_device *phys, u16 addr, + u8 *buf, int len) +{ + struct rmi_spi_data *data = phys->data; + u8 txbuf[4]; + int error; + // pr_info("in function ____%s____ \n", __func__); + + txbuf[0] = SPI_V2_UNIFIED_READ; + txbuf[1] = (addr >> 8) & 0x00FF; + txbuf[2] = addr & 0x00ff; + txbuf[3] = len; + + mutex_lock(&data->page_mutex); + + if (RMI_SPI_PAGE(addr) != data->page) { + error = data->set_page(phys, RMI_SPI_PAGE(addr)); + if (error < 0) + goto exit; + } + + error = rmi_spi_xfer(phys, txbuf, 4, buf, len); + if (error < 0) + goto exit; + error = len; + +exit: + mutex_unlock(&data->page_mutex); + return error; +} + +static int rmi_spi_v2_read(struct rmi_phys_device *phys, u16 addr, u8 *buf) +{ + int error = rmi_spi_v2_read_block(phys, addr, buf, 1); + + return (error == 1) ? 0 : error; +} + +static int rmi_spi_v1_read_block(struct rmi_phys_device *phys, u16 addr, + u8 *buf, int len) +{ + struct rmi_spi_data *data = phys->data; + u8 txbuf[2]; + int error; + // pr_info("in function ____%s____ \n", __func__); + + txbuf[0] = (addr >> 8) | RMI_V1_READ_FLAG; + txbuf[1] = addr; + + mutex_lock(&data->page_mutex); + + if (RMI_SPI_PAGE(addr) != data->page) { + error = data->set_page(phys, RMI_SPI_PAGE(addr)); + if (error < 0) + goto exit; + } + + error = rmi_spi_xfer(phys, txbuf, 2, buf, len); + // pr_info(" back in function %s, rmi_spi_xfer returned %d\n", __func__, error); + + if (error < 0) + goto exit; + error = len; + +exit: + mutex_unlock(&data->page_mutex); + return error; +} + +static int rmi_spi_v1_read(struct rmi_phys_device *phys, u16 addr, u8 *buf) +{ + int error = rmi_spi_v1_read_block(phys, addr, buf, 1); + pr_info("in function ____%s____ \n", __func__); + + return (error == 1) ? 0 : error; +} + +#define RMI_SPI_PAGE_SELECT_WRITE_LENGTH 1 + +static int rmi_spi_v1_set_page(struct rmi_phys_device *phys, u8 page) +{ + struct rmi_spi_data *data = phys->data; + u8 txbuf[] = {RMI_PAGE_SELECT_REGISTER >> 8, + RMI_PAGE_SELECT_REGISTER & 0xFF, page}; + int error; + pr_info("in function ____%s____ \n", __func__); + + error = rmi_spi_xfer(phys, txbuf, sizeof(txbuf), NULL, 0); + if (error < 0) { + dev_err(phys->dev, "Failed to set page select, code: %d.\n", + error); + return error; + } + + data->page = page; + + return RMI_SPI_PAGE_SELECT_WRITE_LENGTH; +} + +static int rmi_spi_v2_set_page(struct rmi_phys_device *phys, u8 page) +{ + struct rmi_spi_data *data = phys->data; + + u8 txbuf[] = {SPI_V2_WRITE, RMI_PAGE_SELECT_REGISTER >> 8, + RMI_PAGE_SELECT_REGISTER & 0xFF, + RMI_SPI_PAGE_SELECT_WRITE_LENGTH, page}; + int error; + pr_info("in function ____%s____ \n", __func__); + + error = rmi_spi_xfer(phys, txbuf, sizeof(txbuf), NULL, 0); + if (error < 0) { + dev_err(phys->dev, "Failed to set page select, code: %d.\n", + error); + return error; + } + + data->page = page; + + return RMI_SPI_PAGE_SELECT_WRITE_LENGTH; +} + + +static int acquire_attn_irq(struct rmi_spi_data *data) +{ + int retval = 0; + pr_info("in function ____%s____ \n", __func__); + pr_info(" irq = %d\n", data->irq); + pr_info(" rmi_spi_hard_irq = 0x%8x\n", rmi_spi_hard_irq); + pr_info(" rmi_spi_irq_thread = 0x%8x\n", rmi_spi_irq_thread); + pr_info(" data->irq_flags = 0x%8x\n", data->irq_flags); + pr_info(" dev_name(data->phys->dev) = %s\n", dev_name(data->phys->dev)); + pr_info(" data->phys = 0x%8x\n", data->phys); + + retval = request_threaded_irq(data->irq, rmi_spi_hard_irq, + rmi_spi_irq_thread, data->irq_flags, + dev_name(data->phys->dev), data->phys); + + pr_info(" retval = = %d\n", retval); + return retval; +} + +static int enable_device(struct rmi_phys_device *phys) +{ + int retval = 0; + + struct rmi_spi_data *data = phys->data; + + if (data->enabled) { + dev_info(phys->dev, "Physical device already enabled.\n"); + return 0; + } + + retval = acquire_attn_irq(data); + if (retval) + goto error_exit; + + data->enabled = true; + dev_info(phys->dev, "Physical device enabled.\n"); + return 0; + +error_exit: + dev_err(phys->dev, "Failed to enable physical device. Code=%d.\n", + retval); + return retval; +} + + +static void disable_device(struct rmi_phys_device *phys) +{ + struct rmi_spi_data *data = phys->data; + + pr_info("in function ____%s____ \n", __func__); + if (!data->enabled) { + dev_warn(phys->dev, "Physical device already disabled.\n"); + return; + } + disable_irq(data->irq); + free_irq(data->irq, data->phys); + + dev_info(phys->dev, "Physical device disabled.\n"); + data->enabled = false; +} + + + +#define DUMMY_READ_SLEEP_US 10 + +static int rmi_spi_check_device(struct rmi_phys_device *rmi_phys) +{ + u8 buf[6]; + int error; + int i; + + pr_info("in function ____%s____ \n", __func__); + + /* Some SPI subsystems return 0 for the very first read you do. So + * we use this dummy read to get that out of the way. + */ + error = rmi_spi_v1_read_block(rmi_phys, PDT_START_SCAN_LOCATION, + buf, sizeof(buf)); + if (error < 0) { + dev_err(rmi_phys->dev, "dummy read failed with %d.\n", error); + return error; + } + udelay(DUMMY_READ_SLEEP_US); + + /* Force page select to 0. + */ + error = rmi_spi_v1_set_page(rmi_phys, 0x00); + if (error < 0) + return error; + + /* Now read the first PDT entry. We know where this is, and if the + * RMI4 device is out there, these 6 bytes will be something other + * than all 0x00 or 0xFF. We need to check for 0x00 and 0xFF, + * because many (maybe all) SPI implementations will return all 0x00 + * or all 0xFF on read if the device is not connected. + */ + error = rmi_spi_v1_read_block(rmi_phys, PDT_START_SCAN_LOCATION, + buf, sizeof(buf)); + if (error < 0) { + dev_err(rmi_phys->dev, "probe read failed with %d.\n", error); + return error; + } + + dev_info(rmi_phys->dev, "probe read succeeded with %d.\n", error); + for (i = 0; i < sizeof(buf); i++) { + if (buf[i] != 0x00 && buf[i] != 0xFF) + return error; + } + + dev_err(rmi_phys->dev, "probe read returned invalid block.\n"); + return -ENODEV; +} + +static int rmi_spi_probe(struct spi_device *spi) +{ + struct rmi_phys_device *rmi_phys; + struct rmi_spi_data *data; + struct rmi_device_platform_data *pdata = spi->dev.platform_data; + u8 buf[2]; + int error; + + pr_info("%s: probe for rmi_spi device\n", __func__); + + if (!pdata) { + dev_err(&spi->dev, "no platform data\n"); + return -EINVAL; + } + + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) + return -EINVAL; + + spi->bits_per_word = 8; + spi->mode = SPI_MODE_3; + error = spi_setup(spi); + if (error < 0) { + dev_err(&spi->dev, "spi_setup failed!\n"); + return error; + } + + rmi_phys = kzalloc(sizeof(struct rmi_phys_device), GFP_KERNEL); + if (!rmi_phys) + return -ENOMEM; + + data = kzalloc(sizeof(struct rmi_spi_data), GFP_KERNEL); + if (!data) { + error = -ENOMEM; + goto err_phys; + } + data->enabled = true; /* We plan to come up enabled. */ + data->irq = gpio_to_irq(pdata->irq); + data->irq_flags = (pdata->irq_polarity == RMI_IRQ_ACTIVE_HIGH) ? + IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; + data->phys = rmi_phys; + + rmi_phys->data = data; + rmi_phys->dev = &spi->dev; + + rmi_phys->write = rmi_spi_v1_write; + rmi_phys->write_block = rmi_spi_v1_write_block; + rmi_phys->read = rmi_spi_v1_read; + rmi_phys->read_block = rmi_spi_v1_read_block; + rmi_phys->enable_device = enable_device; + rmi_phys->disable_device = disable_device; + data->set_page = rmi_spi_v1_set_page; + + rmi_phys->info.proto = spi_v1_proto_name; + + mutex_init(&data->page_mutex); + + pr_info("%s: setting the driverdata on the device\n", __func__); + + dev_set_drvdata(&spi->dev, rmi_phys); + + pr_info("%s: done setting driverdata %s\n", __func__, dev_name(&spi->dev)); + + + pdata->spi_data.block_delay_us = pdata->spi_data.block_delay_us ? + pdata->spi_data.block_delay_us : RMI_SPI_BLOCK_DELAY_US; + pdata->spi_data.read_delay_us = pdata->spi_data.read_delay_us ? + pdata->spi_data.read_delay_us : RMI_SPI_BYTE_DELAY_US; + pdata->spi_data.write_delay_us = pdata->spi_data.write_delay_us ? + pdata->spi_data.write_delay_us : RMI_SPI_BYTE_DELAY_US; + pdata->spi_data.split_read_block_delay_us = + pdata->spi_data.split_read_block_delay_us ? + pdata->spi_data.split_read_block_delay_us : + RMI_SPI_BLOCK_DELAY_US; + pdata->spi_data.split_read_byte_delay_us = + pdata->spi_data.split_read_byte_delay_us ? + pdata->spi_data.split_read_byte_delay_us : + RMI_SPI_BYTE_DELAY_US; + + pr_info("%s configuring GPIOs\n", __func__); + + if (pdata->gpio_config) { + error = pdata->gpio_config(&spi->dev, true); + if (error < 0) { + dev_err(&spi->dev, "Failed to setup GPIOs, code: %d.\n", + error); + goto err_data; + } + } + + error = rmi_spi_check_device(rmi_phys); + if (error < 0) + goto err_data; + + /* check if this is an SPI v2 device */ + dev_info(&spi->dev, "%s: checking SPI version on RMI device\n", __func__); + error = rmi_spi_v1_read_block(rmi_phys, RMI_PROTOCOL_VERSION_ADDRESS, + buf, 2); + + if (error < 0) { + dev_info(&spi->dev, "failed to get SPI version number!\n"); + dev_err(&spi->dev, "failed to get SPI version number!\n"); + goto err_data; + } + + dev_info(&spi->dev, "SPI version is %d", buf[0]); + + if (buf[0] == 1) { + /* SPIv2 */ + rmi_phys->write = rmi_spi_v2_write; + rmi_phys->write_block = rmi_spi_v2_write_block; + rmi_phys->read = rmi_spi_v2_read; + data->set_page = rmi_spi_v2_set_page; + + rmi_phys->info.proto = spi_v2_proto_name; + + if (pdata->irq > 0) { + init_completion(&data->irq_comp); + rmi_phys->read_block = rmi_spi_v2_split_read_block; + } else { + rmi_phys->read_block = rmi_spi_v2_read_block; + } + } else if (buf[0] != 0) { + dev_err(&spi->dev, "Unrecognized SPI version %d.\n", buf[0]); + error = -ENODEV; + goto err_data; + } + + error = rmi_register_phys_device(rmi_phys); + if (error) { + dev_err(&spi->dev, "failed to register physical driver\n"); + goto err_data; + } + + if (pdata->irq > 0) { + error = acquire_attn_irq(data); + if (error < 0) { + dev_err(&spi->dev, "request_threaded_irq failed %d\n", + pdata->irq); + goto err_unregister; + } + } + +#if defined(CONFIG_RMI4_DEV) + pr_info(" CONFIG_RMI4_DEV is defined\n"); + + error = gpio_export(pdata->irq, false); + if (error) { + dev_warn(&spi->dev, "WARNING: Failed to export ATTN gpio!\n"); + error = 0; + } else { + error = gpio_export_link(&(rmi_phys->rmi_dev->dev), "attn", + pdata->irq); + if (error) { + dev_warn(&(rmi_phys->rmi_dev->dev), "WARNING: " + "Failed to symlink ATTN gpio!\n"); + error = 0; + } else { + dev_info(&(rmi_phys->rmi_dev->dev), + "%s: Exported GPIO %d.", __func__, pdata->irq); + } + } +#endif /* CONFIG_RMI4_DEV */ + + dev_info(&spi->dev, "registered RMI SPI driver\n"); + return 0; + +err_unregister: + rmi_unregister_phys_device(rmi_phys); +err_data: + kfree(data); +err_phys: + kfree(rmi_phys); + return error; +} + +static int rmi_spi_remove(struct spi_device *spi) +{ + struct rmi_phys_device *phys = dev_get_drvdata(&spi->dev); + struct rmi_device_platform_data *pd = spi->dev.platform_data; + pr_info("in function ____%s____ \n", __func__); + + rmi_unregister_phys_device(phys); + kfree(phys->data); + kfree(phys); + + if (pd->gpio_config) + pd->gpio_config(&spi->dev, false); + + return 0; +} + +static const struct spi_device_id rmi_id[] = { + { "rmi", 0 }, + { "rmi_spi", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, rmi_id); + +static struct spi_driver rmi_spi_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "rmi_spi", + //.mod_name = "rmi_spi", + //.bus = &spi_bus_type, + }, + .id_table = rmi_id, + .probe = rmi_spi_probe, + .remove = rmi_spi_remove, +}; + +static int __init rmi_spi_init(void) +{ + pr_info("%s: registering synaptics spi driver (ref=124)\n", __func__); + pr_info(" driver.owner = 0x%x\n", (unsigned int)rmi_spi_driver.driver.owner); + pr_info(" driver.name = %s\n", rmi_spi_driver.driver.name); + pr_info(" id_table[0].name = %s\n", rmi_spi_driver.id_table[0].name ); + pr_info(" id_table[1].name = %s\n", rmi_spi_driver.id_table[1].name ); + pr_info(" probe function ptr = 0x%x\n", (unsigned int)rmi_spi_driver.probe ); + + + return spi_register_driver(&rmi_spi_driver); +} + +static void __exit rmi_spi_exit(void) +{ + spi_unregister_driver(&rmi_spi_driver); +} + +MODULE_AUTHOR("Christopher Heiny "); +MODULE_DESCRIPTION("RMI SPI driver"); +MODULE_LICENSE("GPL"); + +module_init(rmi_spi_init); +module_exit(rmi_spi_exit); diff --git a/include/linux/rmi.h b/include/linux/rmi.h new file mode 100644 index 000000000000..84e121d936ac --- /dev/null +++ b/include/linux/rmi.h @@ -0,0 +1,656 @@ +/* + * Copyright (c) 2011 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef _RMI_H +#define _RMI_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + +#ifdef CONFIG_TOUCHSCREEN_SYN_RMI4_SPI + +#define CONFIG_RMI4_BUS +#define CONFIG_RMI4_SPI +#define CONFIG_RMI4_GENERIC +#define CONFIG_RMI4_F09 +#define CONFIG_RMI4_F11 +#define CONFIG_RMI4_F19 +#define CONFIG_RMI4_F34 +#define CONFIG_RMI4_F54 +#define CONFIG_RMI4_DEV + +#endif + +// #define SYNAPTICS_SENSOR_POLL 1 + +/* Permissions for sysfs attributes. Since the permissions policy will change + * on a global basis in the future, rather than edit all sysfs attrs everywhere + * in the driver (and risk screwing that up in the process), we use this handy + * set of #defines. That way when we change the policy for sysfs permissions, + * we only need to change them here. + */ +#define RMI_RO_ATTR S_IRUGO +#define RMI_RW_ATTR (S_IRUGO | S_IWUGO) +#define RMI_WO_ATTR S_IWUGO + +#define PDT_START_SCAN_LOCATION 0x00e9 + +enum rmi_irq_polarity { + RMI_IRQ_ACTIVE_LOW = 0, + RMI_IRQ_ACTIVE_HIGH = 1 +}; + +/** + * struct rmi_f11_axis_alignmen - target axis alignment + * @swap_axes: set to TRUE if desired to swap x- and y-axis + * @flip_x: set to TRUE if desired to flip direction on x-axis + * @flip_y: set to TRUE if desired to flip direction on y-axis + */ +struct rmi_f11_2d_axis_alignment { + bool swap_axes; + bool flip_x; + bool flip_y; + int clip_X_low; + int clip_Y_low; + int clip_X_high; + int clip_Y_high; + int offset_X; + int offset_Y; + int rel_report_enabled; +}; + +/** + * RMI F11 - function control register parameters + * Each register that has a specific bit-field setup has an accompanied + * register definition so that the setting can be chosen as a one-word + * register setting or per-bit setting. + */ +union rmi_f11_2d_ctrl0 { + struct { + u8 reporting_mode:3; + u8 abs_pos_filt:1; + u8 rel_pos_filt:1; + u8 rel_ballistics:1; + u8 dribble:1; + u8 report_beyond_clip:1; + }; + u8 reg; +}; + +union rmi_f11_2d_ctrl1 { + struct { + u8 palm_detect_thres:4; + u8 motion_sensitivity:2; + u8 man_track_en:1; + u8 man_tracked_finger:1; + }; + u8 reg; +}; + +union rmi_f11_2d_ctrl2__3 { + struct { + u8 delta_x_threshold:8; + u8 delta_y_threshold:8; + }; + u8 regs[2]; +}; + +union rmi_f11_2d_ctrl4 { + struct { + u8 velocity:8; + }; + u8 reg; +}; + +union rmi_f11_2d_ctrl5 { + struct { + u8 acceleration:8; + }; + u8 reg; +}; + +union rmi_f11_2d_ctrl6__7 { + struct { + u16 sensor_max_x_pos:12; + }; + u8 regs[2]; +}; + +union rmi_f11_2d_ctrl8__9 { + struct { + u16 sensor_max_y_pos:12; + }; + u8 regs[2]; +}; + +union rmi_f11_2d_ctrl10 { + struct { + u8 single_tap_int_enable:1; + u8 tap_n_hold_int_enable:1; + u8 double_tap_int_enable:1; + u8 early_tap_int_enable:1; + u8 flick_int_enable:1; + u8 press_int_enable:1; + u8 pinch_int_enable:1; + }; + u8 reg; +}; + +union rmi_f11_2d_ctrl11 { + struct { + u8 palm_detect_int_enable:1; + u8 rotate_int_enable:1; + u8 touch_shape_int_enable:1; + u8 scroll_zone_int_enable:1; + u8 multi_finger_scroll_int_enable:1; + }; + u8 reg; +}; + +union rmi_f11_2d_ctrl12 { + struct { + u8 sensor_map:7; + u8 xy_sel:1; + }; + u8 reg; +}; + +union rmi_f11_2d_ctrl14 { + struct { + u8 sens_adjustment:5; + u8 hyst_adjustment:3; + }; + u8 reg; +}; + +/* The configuation is controlled as per register which means that if a register + * is allocated for ctrl configuration one must make sure that all the bits are + * set accordingly for that particular register. + */ +struct rmi_f11_2d_ctrl { + union rmi_f11_2d_ctrl0 *ctrl0; + union rmi_f11_2d_ctrl1 *ctrl1; + union rmi_f11_2d_ctrl2__3 *ctrl2__3; + union rmi_f11_2d_ctrl4 *ctrl4; + union rmi_f11_2d_ctrl5 *ctrl5; + union rmi_f11_2d_ctrl6__7 *ctrl6__7; + union rmi_f11_2d_ctrl8__9 *ctrl8__9; + union rmi_f11_2d_ctrl10 *ctrl10; + union rmi_f11_2d_ctrl11 *ctrl11; + union rmi_f11_2d_ctrl12 *ctrl12; + u8 ctrl12_size; + union rmi_f11_2d_ctrl14 *ctrl14; + u8 *ctrl15; + u8 *ctrl16; + u8 *ctrl17; + u8 *ctrl18; + u8 *ctrl19; +}; + +struct rmi_f19_button_map { + unsigned char nbuttons; + unsigned char *map; +}; + +struct rmi_device_platform_data_spi { + int block_delay_us; + int split_read_block_delay_us; + int read_delay_us; + int write_delay_us; + int split_read_byte_delay_us; + int pre_delay_us; + int post_delay_us; + + void *cs_assert_data; + int (*cs_assert) (const void *cs_assert_data, const bool assert); +}; + +struct rmi_device_platform_data { + char *driver_name; + + int irq; + enum rmi_irq_polarity irq_polarity; + int (*gpio_config)(void *gpio_data, bool configure); + + struct rmi_device_platform_data_spi spi_data; + + /* function handler pdata */ + struct rmi_f11_2d_ctrl *f11_ctrl; + struct rmi_f11_2d_axis_alignment axis_align; + struct rmi_f19_button_map *button_map; + +#ifdef CONFIG_PM + void *pm_data; + int (*pre_suspend) (const void *pm_data); + int (*post_resume) (const void *pm_data); +#endif +}; + +/** + * struct rmi_function_descriptor - RMI function base addresses + * @query_base_addr: The RMI Query base address + * @command_base_addr: The RMI Command base address + * @control_base_addr: The RMI Control base address + * @data_base_addr: The RMI Data base address + * @interrupt_source_count: The number of irqs this RMI function needs + * @function_number: The RMI function number + * + * This struct is used when iterating the Page Description Table. The addresses + * are 16-bit values to include the current page address. + * + */ +struct rmi_function_descriptor { + u16 query_base_addr; + u16 command_base_addr; + u16 control_base_addr; + u16 data_base_addr; + u8 interrupt_source_count; + u8 function_number; + u8 function_version; +}; + +struct rmi_function_container; +struct rmi_device; + +/** + * struct rmi_function_handler - an RMI function handler + * @func: The RMI function number + * @init: Callback for RMI function init + * @attention: Callback for RMI function attention + * @suspend: Callback for function suspend, returns 0 for success. + * @resume: Callback for RMI function resume, returns 0 for success. + * @remove: Callback for RMI function removal + * + * This struct describes the interface of an RMI function. These are + * registered to the bus using the rmi_register_function_driver() call. + * + */ +struct rmi_function_handler { + int func; + int (*init)(struct rmi_function_container *fc); + int (*attention)(struct rmi_function_container *fc, u8 *irq_bits); +#ifdef CONFIG_PM + int (*suspend)(struct rmi_function_container *fc); + int (*resume)(struct rmi_function_container *fc); +#endif + void (*remove)(struct rmi_function_container *fc); +}; + +/** + * struct rmi_function_device - represent an RMI function device + * @dev: The device created + * + * The RMI function device implements the "psuedo" device that represents + * an RMI4 function like function 0x11, function 0x34, etc. and is really + * a placeholder to be able to create sysfs attributes for each function + * in order to facilitate communication between user code and RMI4 functions. + * + */ +struct rmi_function_device { + struct device dev; +}; + +/** + * struct rmi_function_container - an element in a function handler list + * @list: The list + * @fd: The function descriptor of the RMI function + * @rmi_dev: Pointer to the RMI device associated with this function container + * @fh: The callbacks connected to this function + * @num_of_irqs: The number of irqs needed by this function + * @irq_pos: The position in the irq bitfield this function holds + * @data: Private data pointer + * + */ +struct rmi_function_container { + struct list_head list; + + struct rmi_function_descriptor fd; + struct rmi_device *rmi_dev; + struct rmi_function_handler *fh; + struct device dev; + + int num_of_irqs; + int irq_pos; + u8 *irq_mask; + + void *data; +}; +#define to_rmi_function_container(d) \ + container_of(d, struct rmi_function_container, dev); +#define to_rmi_function_device(d) \ + container_of(d, struct rmi_function_device, dev); + + +#ifdef CONFIG_RMI4_DEV + +#define RMI_CHAR_DEV_TMPBUF_SZ 128 +#define RMI_REG_ADDR_PAGE_SELECT 0xFF + +struct rmi_char_dev { + /* mutex for file operation*/ + struct mutex mutex_file_op; + /* main char dev structure */ + struct cdev main_dev; + + /* register address for RMI protocol */ + /* filp->f_pos */ + + /* pointer to the corresponding phys device info for this sensor */ + /* The phys device has the pointers to read, write, etc. */ + struct rmi_phys_device *phys; + /* reference count */ + int ref_count; +}; + +int rmi_char_dev_register(struct rmi_phys_device *phys); +void rmi_char_dev_unregister(struct rmi_phys_device *phys); + +#endif /*CONFIG_RMI4_DEV*/ + + + +/** + * struct rmi_driver - represents an RMI driver + * @driver: Device driver model driver + * @probe: Callback for device probe + * @remove: Callback for device removal + * @shutdown: Callback for device shutdown + * @irq_handler: Callback for handling irqs + * @fh_add: Callback for function handler add + * @fh_remove: Callback for function handler remove + * @get_func_irq_mask: Callback for calculating interrupt mask + * @store_irq_mask: Callback for storing and replacing interrupt mask + * @restore_irq_mask: Callback for restoring previously stored interrupt mask + * @data: Private data pointer + * + * The RMI driver implements a driver on the RMI bus. + * + */ +struct rmi_driver { + struct device_driver driver; + + int (*probe)(struct rmi_device *rmi_dev); + int (*remove)(struct rmi_device *rmi_dev); + void (*shutdown)(struct rmi_device *rmi_dev); + int (*irq_handler)(struct rmi_device *rmi_dev, int irq); + void (*fh_add)(struct rmi_device *rmi_dev, + struct rmi_function_handler *fh); + void (*fh_remove)(struct rmi_device *rmi_dev, + struct rmi_function_handler *fh); + u8* (*get_func_irq_mask)(struct rmi_device *rmi_dev, + struct rmi_function_container *fc); + int (*store_irq_mask)(struct rmi_device *rmi_dev, u8* new_interupts); + int (*restore_irq_mask)(struct rmi_device *rmi_dev); + void *data; +}; +#define to_rmi_driver(d) \ + container_of(d, struct rmi_driver, driver); + +/** struct rmi_phys_info - diagnostic information about the RMI physical + * device, used in the phys sysfs file. + * @proto String indicating the protocol being used. + * @tx_count Number of transmit operations. + * @tx_bytes Number of bytes transmitted. + * @tx_errs Number of errors encountered during transmit operations. + * @rx_count Number of receive operations. + * @rx_bytes Number of bytes received. + * @rx_errs Number of errors encountered during receive operations. + * @att_count Number of times ATTN assertions have been handled. + */ +struct rmi_phys_info { + char *proto; + long tx_count; + long tx_bytes; + long tx_errs; + long rx_count; + long rx_bytes; + long rx_errs; + long attn_count; +}; + +/** + * struct rmi_phys_device - represent an RMI physical device + * @dev: Pointer to the communication device, e.g. i2c or spi + * @rmi_dev: Pointer to the RMI device + * @write: Callback for write + * @write_block: Callback for writing a block of data + * @read: Callback for read + * @read_block: Callback for reading a block of data + * @data: Private data pointer + * + * The RMI physical device implements the glue between different communication + * buses such as I2C and SPI. + * + */ +struct rmi_phys_device { + struct device *dev; + struct rmi_device *rmi_dev; + + int (*write)(struct rmi_phys_device *phys, u16 addr, u8 data); + int (*write_block)(struct rmi_phys_device *phys, u16 addr, u8 *buf, + int len); + int (*read)(struct rmi_phys_device *phys, u16 addr, u8 *buf); + int (*read_block)(struct rmi_phys_device *phys, u16 addr, u8 *buf, + int len); + + int (*enable_device) (struct rmi_phys_device *phys); + void (*disable_device) (struct rmi_phys_device *phys); + + void *data; + + struct rmi_phys_info info; + +#ifdef CONFIG_RMI4_DEV + /* pointer to attention char device and char device */ + struct rmi_char_dev *char_dev; + struct class *rmi_char_device_class; +#endif /*CONFIG_RMI4_DEV*/ +}; + +/** + * struct rmi_device - represents an RMI device + * @dev: The device created for the RMI bus + * @driver: Pointer to associated driver + * @phys: Pointer to the physical interface + * @early_suspend_handler: Pointers to early_suspend and late_resume, if + * configured. + * + * This structs represent an RMI device. + * + */ +struct rmi_device { + struct device dev; + + struct rmi_driver *driver; + struct rmi_phys_device *phys; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend_handler; +#endif +}; +#define to_rmi_device(d) container_of(d, struct rmi_device, dev); +#define to_rmi_platform_data(d) ((d)->phys->dev->platform_data); + +static inline void rmi_set_driverdata(struct rmi_device *d, void *data) +{ + dev_set_drvdata(&d->dev, data); +} + +static inline void *rmi_get_driverdata(struct rmi_device *d) +{ + return dev_get_drvdata(&d->dev); +} + +/** + * rmi_read - RMI read byte + * @d: Pointer to an RMI device + * @addr: The address to read from + * @buf: The read buffer + * + * Reads a byte of data using the underlaying physical protocol in to buf. It + * returns zero or a negative error code. + */ +static inline int rmi_read(struct rmi_device *d, u16 addr, u8 *buf) +{ + return d->phys->read(d->phys, addr, buf); +} + +/** + * rmi_read_block - RMI read block + * @d: Pointer to an RMI device + * @addr: The start address to read from + * @buf: The read buffer + * @len: Length of the read buffer + * + * Reads a block of byte data using the underlaying physical protocol in to buf. + * It returns the amount of bytes read or a negative error code. + */ +static inline int rmi_read_block(struct rmi_device *d, u16 addr, u8 *buf, + int len) +{ + return d->phys->read_block(d->phys, addr, buf, len); +} + +/** + * rmi_write - RMI write byte + * @d: Pointer to an RMI device + * @addr: The address to write to + * @data: The data to write + * + * Writes a byte from buf using the underlaying physical protocol. It + * returns zero or a negative error code. + */ +static inline int rmi_write(struct rmi_device *d, u16 addr, u8 data) +{ + return d->phys->write(d->phys, addr, data); +} + +/** + * rmi_write_block - RMI write block + * @d: Pointer to an RMI device + * @addr: The start address to write to + * @buf: The write buffer + * @len: Length of the write buffer + * + * Writes a block of byte data from buf using the underlaying physical protocol. + * It returns the amount of bytes written or a negative error code. + */ +static inline int rmi_write_block(struct rmi_device *d, u16 addr, u8 *buf, + int len) +{ + return d->phys->write_block(d->phys, addr, buf, len); +} + +/** + * rmi_register_driver - register rmi driver + * @driver: the driver to register + * + * This function registers an RMI driver to the RMI bus. + */ +int rmi_register_driver(struct rmi_driver *driver); + +/** + * rmi_unregister_driver - unregister rmi driver + * @driver: the driver to unregister + * + * This function unregisters an RMI driver to the RMI bus. + */ +void rmi_unregister_driver(struct rmi_driver *driver); + +/** + * rmi_register_phys_device - register a physical device connection + * @phys: the physical driver to register + * + * This function registers a physical driver to the RMI bus. These drivers + * provide a communication layer for the drivers connected to the bus, e.g. + * I2C, SPI and so on. + */ +int rmi_register_phys_device(struct rmi_phys_device *phys); + +/** + * rmi_unregister_phys_device - unregister a physical device connection + * @phys: the physical driver to unregister + * + * This function unregisters a physical driver from the RMI bus. + */ +void rmi_unregister_phys_device(struct rmi_phys_device *phys); + +/** + * rmi_register_function_driver - register an RMI function driver + * @fh: the function handler to register + * + * This function registers support for a new RMI function to the bus. All + * drivers on the bus will be notified of the presence of the new function + * driver. + */ +int rmi_register_function_driver(struct rmi_function_handler *fh); + +/** + * rmi_unregister_function_driver - unregister an RMI function driver + * @fh: the function handler to unregister + * + * This function unregisters a RMI function from the RMI bus. All drivers on + * the bus will be notified of the removal of a function driver. + */ +void rmi_unregister_function_driver(struct rmi_function_handler *fh); + +/** + * rmi_get_function_handler - get a pointer to specified RMI function + * @id: the RMI function id + * + * This function gets the specified RMI function handler from the list of + * supported functions. + */ +struct rmi_function_handler *rmi_get_function_handler(int id); + +/* Utility routine to handle writes to read-only attributes. Hopefully + * this will never happen, but if the user does something stupid, we + * don't want to accept it quietly. + */ +ssize_t rmi_store_error(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +/* Utility routine to handle reads to write-only attributes. Hopefully + * this will never happen, but if the user does something stupid, we + * don't want to accept it quietly. + */ +ssize_t rmi_show_error(struct device *dev, + struct device_attribute *attr, + char *buf); + +/* utility function for bit access of u8*'s */ +void u8_set_bit(u8 *target, int pos); +void u8_clear_bit(u8 *target, int pos); +bool u8_is_set(u8 *target, int pos); +bool u8_is_any_set(u8 *target, int size); +void u8_or(u8 *dest, u8* target1, u8* target2, int size); +void u8_and(u8 *dest, u8* target1, u8* target2, int size); +#endif -- cgit v1.2.3