mirror of
https://github.com/armbian/linux.git
synced 2026-01-06 10:13:00 -08:00
rk30 phone loauqt: add touch screen synaptics s3202 standar drivers
This commit is contained in:
198
drivers/input/touchscreen/rmi4/Kconfig
Executable file
198
drivers/input/touchscreen/rmi4/Kconfig
Executable file
@@ -0,0 +1,198 @@
|
||||
#
|
||||
# RMI4 configuration
|
||||
#
|
||||
config RMI4_BUS
|
||||
bool "Synaptics RMI4 bus support"
|
||||
depends on TOUCHSCREEN_SYNAPTICS_RMI4_I2C_RK
|
||||
help
|
||||
Say Y here if you want to support the Synaptics RMI4 bus.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
This feature is not currently available as
|
||||
a loadable module.
|
||||
|
||||
config RMI4_DEBUG
|
||||
bool "Synaptics RMI4 debugging"
|
||||
depends on RMI4_BUS
|
||||
help
|
||||
Say Y here to enable debug feature in the RMI4 driver.
|
||||
|
||||
Note that the RMI4 driver debug features can generate a lot of
|
||||
output (potentially clogging up your dmesg output) and generally
|
||||
slow down driver operation. It's recommended to enable them only
|
||||
if you are actively developing/debugging RMI4 features.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config RMI4_I2C
|
||||
bool "RMI4 I2C Support"
|
||||
depends on RMI4_BUS && I2C
|
||||
help
|
||||
Say Y here if you want to support RMI4 devices connected to an I2C
|
||||
bus.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
This feature is not currently available as a loadable module.
|
||||
|
||||
config RMI4_I2C_SCL_RATE
|
||||
int "RMI4 I2C SCL RATE Support"
|
||||
depends on RMI4_I2C
|
||||
|
||||
config RMI4_SPI
|
||||
bool "RMI4 SPI Support"
|
||||
depends on RMI4_BUS && SPI
|
||||
help
|
||||
Say Y here if you want to support RMI4 devices connected to an SPI
|
||||
bus.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
This feature is not currently available as a loadable module.
|
||||
|
||||
config RMI4_GENERIC
|
||||
bool "RMI4 Generic driver"
|
||||
depends on RMI4_BUS
|
||||
help
|
||||
Say Y here if you want to support generic RMI4 devices.
|
||||
|
||||
This is pretty much required if you want to do anything useful with
|
||||
your RMI device.
|
||||
|
||||
This feature is not currently available as a loadable module.
|
||||
|
||||
|
||||
config RMI4_F1A
|
||||
tristate "RMI4 Function 1A (capacitive button sensor)"
|
||||
depends on RMI4_BUS && RMI4_GENERIC
|
||||
help
|
||||
Say Y here if you want to add support for RMI4 function 1A.
|
||||
|
||||
Function 1A provides self testing for touchscreens and touchpads.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rmi-f1a.
|
||||
|
||||
config RMI4_F09
|
||||
tristate "RMI4 Function 09 (self testing)"
|
||||
depends on RMI4_BUS && RMI4_GENERIC
|
||||
help
|
||||
Say Y here if you want to add support for RMI4 function 09.
|
||||
|
||||
Function 09 provides self testing for touchscreens and touchpads.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rmi-f09.
|
||||
|
||||
config RMI4_F11
|
||||
tristate "RMI4 Function 11 (2D pointing)"
|
||||
depends on RMI4_BUS && RMI4_GENERIC
|
||||
help
|
||||
Say Y here if you want to add support for RMI4 function 11.
|
||||
|
||||
Function 11 provides 2D multifinger pointing for touchscreens and
|
||||
touchpads. For sensors that support relative pointing, F11 also
|
||||
provides mouse input.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rmi-f11.
|
||||
|
||||
config RMI4_F11_PEN
|
||||
bool "RMI4 F11 Pen Support"
|
||||
depends on RMI4_F11
|
||||
help
|
||||
Say Y here to add support for pen input to RMI4 function 11.
|
||||
|
||||
If this feature is enabled, when pen inputs are detected they
|
||||
will be reported to the input stream as MT_TOOL_PEN. Otherwise,
|
||||
pens will be treated the same as fingers.
|
||||
|
||||
Not all UI implementations deal gracefully with pen discrimination.
|
||||
If your system is not recognizing pen touches and you know your
|
||||
sensor supports pen input, you probably want to turn this feature
|
||||
off.
|
||||
|
||||
config RMI4_VIRTUAL_BUTTON
|
||||
tristate "RMI4 Vitual Button"
|
||||
depends on RMI4_F11
|
||||
help
|
||||
Say Y here if you want to add support for RMI4 virtual button to F11.
|
||||
|
||||
The virtual button feature implement the virtual button device in
|
||||
certain RMI4 touch sensors.
|
||||
|
||||
This works only if your sensor supports F11 gestures.
|
||||
|
||||
config RMI4_F17
|
||||
tristate "RMI4 Function 17 (pointing sticks)"
|
||||
depends on RMI4_BUS && RMI4_GENERIC
|
||||
help
|
||||
Say Y here if you want to add support for RMI4 function 17.
|
||||
|
||||
Function 19 provides support for capacitive and resistive
|
||||
pointing sticks.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rmi-f17.
|
||||
|
||||
config RMI4_F19
|
||||
tristate "RMI4 Function 19 (0D pointing)"
|
||||
depends on RMI4_BUS && RMI4_GENERIC
|
||||
help
|
||||
Say Y here if you want to add support for RMI4 function 19.
|
||||
|
||||
Function 19 provides support for capacitive buttons for sensors
|
||||
that implement capacitive buttons.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rmi-f19.
|
||||
|
||||
config RMI4_F21
|
||||
tristate "RMI4 Function 21 (2D Force)"
|
||||
depends on RMI4_BUS && RMI4_GENERIC
|
||||
help
|
||||
Say Y here if you want to add support for RMI4 function 21.
|
||||
|
||||
Function 21 provides 2D Force Sensing for ForcePad products.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rmi-f21.
|
||||
|
||||
config RMI4_F34
|
||||
tristate "RMI4 Function 34 (device reflash)"
|
||||
depends on RMI4_BUS && RMI4_GENERIC
|
||||
help
|
||||
Say Y here if you want to add support for RMI4 function 34.
|
||||
|
||||
Function 34 provides firmware upgrade capability for your sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rmi-f34.
|
||||
config RMI4_REFLASH_WHEN_BOOT
|
||||
tristate "RMI4 Function 34 (device reflash when system boot)"
|
||||
depends on RMI4_BUS && RMI4_GENERIC
|
||||
|
||||
config RMI4_F54
|
||||
tristate "RMI4 Function 54 (analog diagnostics)"
|
||||
depends on RMI4_BUS && RMI4_GENERIC
|
||||
help
|
||||
Say Y here if you want to add support for RMI4 function 54.
|
||||
|
||||
Function 54 provides access to various diagnostic features in
|
||||
certain RMI4 touch sensors.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rmi-f54.
|
||||
|
||||
config RMI4_DEV
|
||||
tristate "Synaptics direct RMI device support (rmidev)"
|
||||
depends on GPIO_SYSFS && (RMI4_I2C || RMI4_SPI)
|
||||
help
|
||||
Say Y here to add support for rmidev.
|
||||
|
||||
The rmidev feature implements a character device providing access
|
||||
to RMI4 sensor register maps.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rmi-dev.
|
||||
34
drivers/input/touchscreen/rmi4/Makefile
Executable file
34
drivers/input/touchscreen/rmi4/Makefile
Executable file
@@ -0,0 +1,34 @@
|
||||
obj-$(CONFIG_RMI4_BUS) += rmi_bus.o
|
||||
obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o
|
||||
obj-$(CONFIG_RMI4_SPI) += rmi_spi.o
|
||||
obj-$(CONFIG_RMI4_GENERIC) += rmi_driver.o rmi_f01.o
|
||||
obj-$(CONFIG_RMI4_F09) += rmi_f09.o
|
||||
obj-$(CONFIG_RMI4_F1A) += rmi_f1a.o
|
||||
obj-$(CONFIG_RMI4_F11) += rmi_f11.o
|
||||
obj-$(CONFIG_RMI4_F17) += rmi_f17.o
|
||||
obj-$(CONFIG_RMI4_F19) += rmi_f19.o
|
||||
obj-$(CONFIG_RMI4_F21) += rmi_f21.o
|
||||
obj-$(CONFIG_RMI4_F34) += rmi_f34.o
|
||||
obj-$(CONFIG_RMI4_F54) += rmi_f54.o
|
||||
obj-$(CONFIG_RMI4_DEV) += rmi_dev.o
|
||||
obj-y += rmi_reflash.o
|
||||
|
||||
ifeq ($(KERNELRELEASE),)
|
||||
|
||||
# KERNELDIR ?= /home/<AndroidKernelDirectory>
|
||||
PWD := $(shell pwd)
|
||||
|
||||
.PHONY: build clean
|
||||
|
||||
build:
|
||||
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
|
||||
|
||||
clean:
|
||||
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c
|
||||
else
|
||||
|
||||
$(info Building with KERNELRELEASE = ${KERNELRELEASE})
|
||||
obj-m += rmi_dev.o
|
||||
|
||||
endif
|
||||
|
||||
482
drivers/input/touchscreen/rmi4/rmi_bus.c
Executable file
482
drivers/input/touchscreen/rmi4/rmi_bus.c
Executable file
@@ -0,0 +1,482 @@
|
||||
/*
|
||||
* 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 <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/rmi.h>
|
||||
#include <linux/types.h>
|
||||
#ifdef CONFIG_RMI4_DEBUG
|
||||
#include <linux/debugfs.h>
|
||||
#endif
|
||||
#include "rmi_driver.h"
|
||||
DEFINE_MUTEX(rmi_bus_mutex);
|
||||
|
||||
static struct rmi_function_list {
|
||||
struct list_head list;
|
||||
struct rmi_function_handler *fh;
|
||||
} rmi_supported_functions;
|
||||
|
||||
static struct rmi_character_driver_list {
|
||||
struct list_head list;
|
||||
struct rmi_char_driver *cd;
|
||||
} rmi_character_drivers;
|
||||
|
||||
static atomic_t physical_device_count;
|
||||
|
||||
#ifdef CONFIG_RMI4_DEBUG
|
||||
static struct dentry *rmi_debugfs_root;
|
||||
#endif
|
||||
|
||||
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;
|
||||
|
||||
rmi_driver = to_rmi_driver(driver);
|
||||
rmi_dev = to_rmi_device(dev);
|
||||
pdata = to_rmi_platform_data(rmi_dev);
|
||||
dev_dbg(dev, "%s: Matching %s.\n", __func__, pdata->sensor_name);
|
||||
|
||||
if (!strcmp(pdata->driver_name, rmi_driver->driver.name)) {
|
||||
rmi_dev->driver = rmi_driver;
|
||||
dev_dbg(dev, "%s: Match %s to %s succeeded.\n", __func__,
|
||||
pdata->driver_name, rmi_driver->driver.name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
dev_vdbg(dev, "%s: Match %s to %s failed.\n", __func__,
|
||||
pdata->driver_name, rmi_driver->driver.name);
|
||||
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;
|
||||
|
||||
if (pm && pm->resume)
|
||||
return pm->resume(dev);
|
||||
else if (dev->driver && dev->driver->resume)
|
||||
return dev->driver->resume(dev);
|
||||
#else
|
||||
if (dev->driver && dev->driver->resume)
|
||||
return dev->driver->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);
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
static void release_rmidev_device(struct device *dev) {
|
||||
device_unregister(dev);
|
||||
}
|
||||
|
||||
int rmi_register_phys_device(struct rmi_phys_device *phys)
|
||||
{
|
||||
struct rmi_device_platform_data *pdata = phys->dev->platform_data;
|
||||
struct rmi_device *rmi_dev;
|
||||
|
||||
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;
|
||||
|
||||
rmi_dev->number = atomic_inc_return(&physical_device_count) - 1;
|
||||
rmi_dev->dev.release = release_rmidev_device;
|
||||
|
||||
dev_set_name(&rmi_dev->dev, "sensor%02d", rmi_dev->number);
|
||||
dev_dbg(phys->dev, "%s: Registered %s as %s.\n", __func__,
|
||||
pdata->sensor_name, dev_name(&rmi_dev->dev));
|
||||
|
||||
#ifdef CONFIG_RMI4_DEBUG
|
||||
if (rmi_debugfs_root) {
|
||||
rmi_dev->debugfs_root = debugfs_create_dir(
|
||||
dev_name(&rmi_dev->dev), rmi_debugfs_root);
|
||||
if (!rmi_dev->debugfs_root)
|
||||
dev_err(&rmi_dev->dev, "Failed to create debugfs root.\n");
|
||||
}
|
||||
#endif
|
||||
phys->rmi_dev = rmi_dev;
|
||||
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;
|
||||
|
||||
#ifdef CONFIG_RMI4_DEBUG
|
||||
if (rmi_dev->debugfs_root)
|
||||
debugfs_remove(rmi_dev->debugfs_root);
|
||||
#endif
|
||||
|
||||
kfree(rmi_dev);
|
||||
}
|
||||
EXPORT_SYMBOL(rmi_unregister_phys_device);
|
||||
|
||||
int rmi_register_driver(struct rmi_driver *driver)
|
||||
{
|
||||
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);
|
||||
|
||||
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;
|
||||
INIT_LIST_HEAD(&entry->list);
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
/* notify devices of the removal of the function handler */
|
||||
bus_for_each_dev(&rmi_bus_type, NULL, fh, __rmi_bus_fh_remove);
|
||||
|
||||
if (list_empty(&rmi_supported_functions.list))
|
||||
return;
|
||||
|
||||
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;
|
||||
|
||||
if (list_empty(&rmi_supported_functions.list))
|
||||
return NULL;
|
||||
|
||||
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 void rmi_release_character_device(struct device *dev)
|
||||
{
|
||||
dev_dbg(dev, "%s: Called.\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
static int rmi_register_character_device(struct device *dev, void *data)
|
||||
{
|
||||
struct rmi_device *rmi_dev;
|
||||
struct rmi_char_driver *char_driver = data;
|
||||
struct rmi_char_device *char_dev;
|
||||
int retval;
|
||||
|
||||
dev_dbg(dev, "Attaching character device.\n");
|
||||
rmi_dev = to_rmi_device(dev);
|
||||
if (char_driver->match && !char_driver->match(rmi_dev))
|
||||
return 0;
|
||||
|
||||
if (!char_driver->init) {
|
||||
dev_err(dev, "ERROR: No init() function in %s.\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
char_dev = kzalloc(sizeof(struct rmi_char_device), GFP_KERNEL);
|
||||
if (!char_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
char_dev->rmi_dev = rmi_dev;
|
||||
char_dev->driver = char_driver;
|
||||
|
||||
char_dev->dev.parent = dev;
|
||||
char_dev->dev.release = rmi_release_character_device;
|
||||
char_dev->dev.driver = &char_driver->driver;
|
||||
retval = device_register(&char_dev->dev);
|
||||
if (!retval) {
|
||||
dev_err(dev, "Failed to register character device.\n");
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
retval = char_driver->init(char_dev);
|
||||
if (retval) {
|
||||
dev_err(dev, "Failed to initialize character device.\n");
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
mutex_lock(&rmi_bus_mutex);
|
||||
list_add_tail(&char_dev->list, &char_driver->devices);
|
||||
mutex_unlock(&rmi_bus_mutex);
|
||||
dev_info(&char_dev->dev, "Registered a device.\n");
|
||||
return retval;
|
||||
|
||||
error_exit:
|
||||
kfree(char_dev);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int rmi_register_character_driver(struct rmi_char_driver *char_driver)
|
||||
{
|
||||
struct rmi_character_driver_list *entry;
|
||||
int retval;
|
||||
|
||||
pr_debug("%s: Registering character driver %s.\n", __func__,
|
||||
char_driver->driver.name);
|
||||
|
||||
char_driver->driver.bus = &rmi_bus_type;
|
||||
INIT_LIST_HEAD(&char_driver->devices);
|
||||
retval = driver_register(&char_driver->driver);
|
||||
if (retval) {
|
||||
pr_err("%s: Failed to register %s, code: %d.\n", __func__,
|
||||
char_driver->driver.name, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
entry = kzalloc(sizeof(struct rmi_character_driver_list), GFP_KERNEL);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
entry->cd = char_driver;
|
||||
|
||||
mutex_lock(&rmi_bus_mutex);
|
||||
list_add_tail(&entry->list, &rmi_character_drivers.list);
|
||||
mutex_unlock(&rmi_bus_mutex);
|
||||
|
||||
/* notify devices of the removal of the function handler */
|
||||
bus_for_each_dev(&rmi_bus_type, NULL, char_driver,
|
||||
rmi_register_character_device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rmi_register_character_driver);
|
||||
|
||||
|
||||
int rmi_unregister_character_driver(struct rmi_char_driver *char_driver)
|
||||
{
|
||||
struct rmi_character_driver_list *entry, *n;
|
||||
struct rmi_char_device *char_dev, *m;
|
||||
pr_debug("%s: Unregistering character driver %s.\n", __func__,
|
||||
char_driver->driver.name);
|
||||
|
||||
mutex_lock(&rmi_bus_mutex);
|
||||
list_for_each_entry_safe(char_dev, m, &char_driver->devices,
|
||||
list) {
|
||||
list_del(&char_dev->list);
|
||||
char_dev->driver->remove(char_dev);
|
||||
}
|
||||
list_for_each_entry_safe(entry, n, &rmi_character_drivers.list,
|
||||
list) {
|
||||
if (entry->cd == char_driver) {
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&rmi_bus_mutex);
|
||||
|
||||
driver_unregister(&char_driver->driver);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rmi_unregister_character_driver);
|
||||
|
||||
static int __init rmi_bus_init(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
mutex_init(&rmi_bus_mutex);
|
||||
INIT_LIST_HEAD(&rmi_supported_functions.list);
|
||||
INIT_LIST_HEAD(&rmi_character_drivers.list);
|
||||
|
||||
#ifdef CONFIG_RMI4_DEBUG
|
||||
rmi_debugfs_root = debugfs_create_dir(rmi_bus_type.name, NULL);
|
||||
if (!rmi_debugfs_root)
|
||||
pr_err("%s: Failed to create debugfs root.\n", __func__);
|
||||
else if (IS_ERR(rmi_debugfs_root)) {
|
||||
pr_err("%s: Kernel may not contain debugfs support, code=%ld\n",
|
||||
__func__, PTR_ERR(rmi_debugfs_root));
|
||||
rmi_debugfs_root = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
error = bus_register(&rmi_bus_type);
|
||||
if (error < 0) {
|
||||
pr_err("%s: error registering the RMI bus: %d\n", __func__,
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
pr_debug("%s: successfully registered RMI bus.\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit rmi_bus_exit(void)
|
||||
{
|
||||
/* We should only ever get here if all drivers are unloaded, so
|
||||
* all we have to do at this point is unregister ourselves.
|
||||
*/
|
||||
#ifdef CONFIG_RMI4_DEBUG
|
||||
if (rmi_debugfs_root)
|
||||
debugfs_remove(rmi_debugfs_root);
|
||||
#endif
|
||||
bus_unregister(&rmi_bus_type);
|
||||
}
|
||||
|
||||
module_init(rmi_bus_init);
|
||||
module_exit(rmi_bus_exit);
|
||||
|
||||
MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com");
|
||||
MODULE_DESCRIPTION("RMI bus");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(RMI_DRIVER_VERSION);
|
||||
448
drivers/input/touchscreen/rmi4/rmi_dev.c
Executable file
448
drivers/input/touchscreen/rmi4/rmi_dev.c
Executable file
@@ -0,0 +1,448 @@
|
||||
/*
|
||||
* 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 <linux/fs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/syscalls.h>
|
||||
|
||||
#include <linux/rmi.h>
|
||||
#include "rmi_driver.h"
|
||||
|
||||
#define CHAR_DEVICE_NAME "rmi"
|
||||
#define DEVICE_CLASS_NAME "rmidev"
|
||||
|
||||
#define RMI_CHAR_DEV_TMPBUF_SZ 128
|
||||
#define RMI_REG_ADDR_PAGE_SELECT 0xFF
|
||||
#define REG_ADDR_LIMIT 0xFFFF
|
||||
|
||||
struct rmidev_data {
|
||||
/* mutex for file operation*/
|
||||
struct mutex file_mutex;
|
||||
/* main char dev structure */
|
||||
struct cdev main_dev;
|
||||
|
||||
/* pointer to the corresponding RMI4 device. We use this to do */
|
||||
/* read, write, etc. */
|
||||
struct rmi_device *rmi_dev;
|
||||
/* reference count */
|
||||
int ref_count;
|
||||
|
||||
struct class *device_class;
|
||||
};
|
||||
|
||||
/*store dynamically allocated major number of char device*/
|
||||
static int rmidev_major_num;
|
||||
|
||||
|
||||
static struct class *rmidev_device_class;
|
||||
|
||||
|
||||
/* file operations for RMI char device */
|
||||
|
||||
/*
|
||||
* rmidev_llseek: - use to setup register address
|
||||
*
|
||||
* @filp: file structure for seek
|
||||
* @off: offset
|
||||
* if whence == SEEK_SET,
|
||||
* high 16 bits: page address
|
||||
* low 16 bits: register address
|
||||
*
|
||||
* if whence == SEEK_CUR,
|
||||
* offset from current position
|
||||
*
|
||||
* if whence == SEEK_END,
|
||||
* offset from END(0xFFFF)
|
||||
*
|
||||
* @whence: SEEK_SET , SEEK_CUR or SEEK_END
|
||||
*/
|
||||
static loff_t rmidev_llseek(struct file *filp, loff_t off, int whence)
|
||||
{
|
||||
loff_t newpos;
|
||||
struct rmidev_data *data = filp->private_data;
|
||||
|
||||
if (IS_ERR(data)) {
|
||||
pr_err("%s: pointer of char device is invalid", __func__);
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
mutex_lock(&(data->file_mutex));
|
||||
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
newpos = off;
|
||||
break;
|
||||
|
||||
case SEEK_CUR:
|
||||
newpos = filp->f_pos + off;
|
||||
break;
|
||||
|
||||
case SEEK_END:
|
||||
newpos = REG_ADDR_LIMIT + off;
|
||||
break;
|
||||
|
||||
default: /* can't happen */
|
||||
newpos = -EINVAL;
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
if (newpos < 0 || newpos > REG_ADDR_LIMIT) {
|
||||
dev_err(&data->rmi_dev->dev, "newpos 0x%04x is invalid.\n",
|
||||
(unsigned int)newpos);
|
||||
newpos = -EINVAL;
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
filp->f_pos = newpos;
|
||||
|
||||
clean_up:
|
||||
mutex_unlock(&(data->file_mutex));
|
||||
return newpos;
|
||||
}
|
||||
|
||||
/*
|
||||
* rmidev_read: - use to read data from RMI stream
|
||||
*
|
||||
* @filp: file structure for read
|
||||
* @buf: user-level buffer pointer
|
||||
*
|
||||
* @count: number of byte read
|
||||
* @f_pos: offset (starting register address)
|
||||
*
|
||||
* @return number of bytes read into user buffer (buf) if succeeds
|
||||
* negative number if error occurs.
|
||||
*/
|
||||
static ssize_t rmidev_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *f_pos)
|
||||
{
|
||||
struct rmidev_data *data = filp->private_data;
|
||||
ssize_t retval = 0;
|
||||
unsigned char tmpbuf[count+1];
|
||||
|
||||
/* 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(data)) {
|
||||
pr_err("%s: pointer of char device is invalid", __func__);
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
mutex_lock(&(data->file_mutex));
|
||||
|
||||
retval = rmi_read_block(data->rmi_dev, *f_pos, tmpbuf, count);
|
||||
|
||||
if (retval < 0)
|
||||
goto clean_up;
|
||||
|
||||
if (copy_to_user(buf, tmpbuf, count))
|
||||
retval = -EFAULT;
|
||||
else
|
||||
*f_pos += retval;
|
||||
|
||||
clean_up:
|
||||
|
||||
mutex_unlock(&(data->file_mutex));
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* rmidev_write: - use to write data into RMI stream
|
||||
*
|
||||
* @filep : file structure for write
|
||||
* @buf: user-level buffer pointer contains data to be written
|
||||
* @count: number of byte be be written
|
||||
* @f_pos: offset (starting register address)
|
||||
*
|
||||
* @return number of bytes written from user buffer (buf) if succeeds
|
||||
* negative number if error occurs.
|
||||
*/
|
||||
static ssize_t rmidev_write(struct file *filp, const char __user *buf,
|
||||
size_t count, loff_t *f_pos)
|
||||
{
|
||||
struct rmidev_data *data = filp->private_data;
|
||||
ssize_t retval = 0;
|
||||
unsigned char tmpbuf[count+1];
|
||||
|
||||
/* 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(data)) {
|
||||
pr_err("%s: pointer of char device is invalid", __func__);
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
if (copy_from_user(tmpbuf, buf, count))
|
||||
return -EFAULT;
|
||||
|
||||
mutex_lock(&(data->file_mutex));
|
||||
|
||||
retval = rmi_write_block(data->rmi_dev, *f_pos, tmpbuf, count);
|
||||
|
||||
if (retval >= 0)
|
||||
*f_pos += count;
|
||||
|
||||
mutex_unlock(&(data->file_mutex));
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* rmidev_open: - get a new handle for from RMI stream
|
||||
* @inp : inode struture
|
||||
* @filp: file structure for read/write
|
||||
*
|
||||
* @return 0 if succeeds
|
||||
*/
|
||||
static int rmidev_open(struct inode *inp, struct file *filp)
|
||||
{
|
||||
struct rmidev_data *data = container_of(inp->i_cdev,
|
||||
struct rmidev_data, main_dev);
|
||||
int retval = 0;
|
||||
|
||||
filp->private_data = data;
|
||||
|
||||
if (!data->rmi_dev)
|
||||
return -EACCES;
|
||||
|
||||
mutex_lock(&(data->file_mutex));
|
||||
if (data->ref_count < 1)
|
||||
data->ref_count++;
|
||||
else
|
||||
retval = -EACCES;
|
||||
|
||||
mutex_unlock(&(data->file_mutex));
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* rmidev_release: - release an existing handle
|
||||
* @inp: inode structure
|
||||
* @filp: file structure for read/write
|
||||
*
|
||||
* @return 0 if succeeds
|
||||
*/
|
||||
static int rmidev_release(struct inode *inp, struct file *filp)
|
||||
{
|
||||
struct rmidev_data *data = container_of(inp->i_cdev,
|
||||
struct rmidev_data, main_dev);
|
||||
|
||||
if (!data->rmi_dev)
|
||||
return -EACCES;
|
||||
|
||||
mutex_lock(&(data->file_mutex));
|
||||
|
||||
data->ref_count--;
|
||||
if (data->ref_count < 0)
|
||||
data->ref_count = 0;
|
||||
|
||||
mutex_unlock(&(data->file_mutex));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations rmidev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = rmidev_llseek,
|
||||
.read = rmidev_read,
|
||||
.write = rmidev_write,
|
||||
.open = rmidev_open,
|
||||
.release = rmidev_release,
|
||||
};
|
||||
|
||||
/*
|
||||
* rmidev_device_cleanup - release memory or unregister driver
|
||||
* @rmidev_data: instance data for a particular device.
|
||||
*
|
||||
*/
|
||||
static void rmidev_device_cleanup(struct rmidev_data *data)
|
||||
{
|
||||
dev_t devno;
|
||||
|
||||
/* Get rid of our char dev entries */
|
||||
if (data) {
|
||||
devno = data->main_dev.dev;
|
||||
|
||||
if (data->device_class)
|
||||
device_destroy(data->device_class, devno);
|
||||
|
||||
cdev_del(&data->main_dev);
|
||||
kfree(data);
|
||||
|
||||
/* cleanup_module is never called if registering failed */
|
||||
unregister_chrdev_region(devno, 1);
|
||||
pr_debug("%s: rmidev device is removed\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* rmi_char_devnode - return device permission
|
||||
*
|
||||
* @dev: char device structure
|
||||
* @mode: file permission
|
||||
*
|
||||
*/
|
||||
static char *rmi_char_devnode(struct device *dev, mode_t *mode)
|
||||
{
|
||||
if (!mode)
|
||||
return NULL;
|
||||
/**mode = 0666*/
|
||||
*mode = (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
|
||||
|
||||
return kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev));
|
||||
}
|
||||
|
||||
static int rmidev_init_device(struct rmi_char_device *cd)
|
||||
{
|
||||
struct rmi_device *rmi_dev = cd->rmi_dev;
|
||||
struct rmidev_data *data;
|
||||
dev_t dev_no;
|
||||
int retval;
|
||||
struct device *device_ptr;
|
||||
|
||||
if (rmidev_major_num) {
|
||||
dev_no = MKDEV(rmidev_major_num, cd->rmi_dev->number);
|
||||
retval = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME);
|
||||
} else {
|
||||
retval = alloc_chrdev_region(&dev_no, 0, 1, CHAR_DEVICE_NAME);
|
||||
/* let kernel allocate a major for us */
|
||||
rmidev_major_num = MAJOR(dev_no);
|
||||
dev_info(&rmi_dev->dev, "Major number of rmidev: %d\n",
|
||||
rmidev_major_num);
|
||||
}
|
||||
if (retval < 0) {
|
||||
dev_err(&rmi_dev->dev,
|
||||
"Failed to get minor dev number %d, code %d.\n",
|
||||
cd->rmi_dev->number, retval);
|
||||
return retval;
|
||||
} else
|
||||
dev_info(&rmi_dev->dev, "Allocated rmidev %d %d.\n",
|
||||
MAJOR(dev_no), MINOR(dev_no));
|
||||
|
||||
data = kzalloc(sizeof(struct rmidev_data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
dev_err(&rmi_dev->dev, "Failed to allocate rmidev_data.\n");
|
||||
/* unregister the char device region */
|
||||
__unregister_chrdev(rmidev_major_num, MINOR(dev_no), 1,
|
||||
CHAR_DEVICE_NAME);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mutex_init(&data->file_mutex);
|
||||
|
||||
data->rmi_dev = cd->rmi_dev;
|
||||
cd->data = data;
|
||||
|
||||
cdev_init(&data->main_dev, &rmidev_fops);
|
||||
|
||||
retval = cdev_add(&data->main_dev, dev_no, 1);
|
||||
if (retval) {
|
||||
dev_err(&cd->rmi_dev->dev, "Error %d adding rmi_char_dev.\n",
|
||||
retval);
|
||||
rmidev_device_cleanup(data);
|
||||
return retval;
|
||||
}
|
||||
|
||||
dev_set_name(&cd->dev, "rmidev%d", MINOR(dev_no));
|
||||
data->device_class = rmidev_device_class;
|
||||
device_ptr = device_create(
|
||||
data->device_class,
|
||||
NULL, dev_no, NULL,
|
||||
CHAR_DEVICE_NAME"%d",
|
||||
MINOR(dev_no));
|
||||
|
||||
if (IS_ERR(device_ptr)) {
|
||||
dev_err(&cd->rmi_dev->dev, "Failed to create rmi device.\n");
|
||||
rmidev_device_cleanup(data);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rmidev_remove_device(struct rmi_char_device *cd)
|
||||
{
|
||||
struct rmidev_data *data;
|
||||
|
||||
dev_dbg(&cd->dev, "%s: removing an rmidev device.\n", __func__);
|
||||
if (!cd)
|
||||
return;
|
||||
|
||||
data = cd->data;
|
||||
if (data)
|
||||
rmidev_device_cleanup(data);
|
||||
}
|
||||
|
||||
static struct rmi_char_driver rmidev_driver = {
|
||||
.driver = {
|
||||
.name = "rmidev",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
|
||||
.init = rmidev_init_device,
|
||||
.remove = rmidev_remove_device,
|
||||
};
|
||||
|
||||
static int __init rmidev_init(void)
|
||||
{
|
||||
int error = 0;
|
||||
pr_debug("%s: rmi_dev initialization.\n", __func__);
|
||||
|
||||
/* create device node */
|
||||
rmidev_device_class = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
|
||||
|
||||
if (IS_ERR(rmidev_device_class)) {
|
||||
pr_err("%s: ERROR - Failed to create /dev/%s.\n", __func__,
|
||||
CHAR_DEVICE_NAME);
|
||||
return -ENODEV;
|
||||
}
|
||||
/* setup permission */
|
||||
rmidev_device_class->devnode = rmi_char_devnode;
|
||||
|
||||
error = rmi_register_character_driver(&rmidev_driver);
|
||||
if (error)
|
||||
class_destroy(rmidev_device_class);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void __exit rmidev_exit(void)
|
||||
{
|
||||
pr_debug("%s: exiting.\n", __func__);
|
||||
rmi_unregister_character_driver(&rmidev_driver);
|
||||
class_destroy(rmidev_device_class);
|
||||
}
|
||||
|
||||
module_init(rmidev_init);
|
||||
module_exit(rmidev_exit);
|
||||
|
||||
MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
|
||||
MODULE_DESCRIPTION("RMI4 Char Device");
|
||||
MODULE_LICENSE("GPL");
|
||||
1616
drivers/input/touchscreen/rmi4/rmi_driver.c
Executable file
1616
drivers/input/touchscreen/rmi4/rmi_driver.c
Executable file
File diff suppressed because it is too large
Load Diff
405
drivers/input/touchscreen/rmi4/rmi_driver.h
Executable file
405
drivers/input/touchscreen/rmi4/rmi_driver.h
Executable file
@@ -0,0 +1,405 @@
|
||||
/*
|
||||
* 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_VERSION "1.3"
|
||||
|
||||
#define RMI_PRODUCT_ID_LENGTH 10
|
||||
#define RMI_PRODUCT_INFO_LENGTH 2
|
||||
#define RMI_DATE_CODE_LENGTH 3
|
||||
|
||||
#include <linux/ctype.h>
|
||||
/* Sysfs related macros */
|
||||
|
||||
/* You must define FUNCTION_DATA and FNUM to use these functions. */
|
||||
#define RMI4_SYSFS_DEBUG defined(CONFIG_RMI4_DEBUG) || defined(CONFIG_ANDROID))
|
||||
|
||||
#if defined(FNUM) && defined(FUNCTION_DATA)
|
||||
|
||||
#define tricat(x,y,z) tricat_(x,y,z)
|
||||
|
||||
#define tricat_(x,y,z) x##y##z
|
||||
|
||||
#define show_union_struct_prototype(propname)\
|
||||
static ssize_t tricat(rmi_fn_,FNUM,_##propname##_show)(\
|
||||
struct device *dev,\
|
||||
struct device_attribute *attr,\
|
||||
char *buf);\
|
||||
\
|
||||
DEVICE_ATTR(propname, RMI_RO_ATTR,\
|
||||
tricat(rmi_fn_,FNUM,_##propname##_show),\
|
||||
rmi_store_error);
|
||||
|
||||
#define store_union_struct_prototype(propname)\
|
||||
static ssize_t tricat(rmi_fn_,FNUM,_##propname##_store)(\
|
||||
struct device *dev,\
|
||||
struct device_attribute *attr,\
|
||||
const char *buf, size_t count);\
|
||||
\
|
||||
DEVICE_ATTR(propname, RMI_WO_ATTR,\
|
||||
rmi_show_error,\
|
||||
tricat(rmi_fn_, FNUM, _##propname##_store));
|
||||
|
||||
|
||||
#define show_store_union_struct_prototype(propname)\
|
||||
static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(\
|
||||
struct device *dev,\
|
||||
struct device_attribute *attr,\
|
||||
char *buf);\
|
||||
\
|
||||
static ssize_t tricat(rmi_fn_, FNUM, _##propname##_store)(\
|
||||
struct device *dev,\
|
||||
struct device_attribute *attr,\
|
||||
const char *buf, size_t count);\
|
||||
\
|
||||
DEVICE_ATTR(propname, RMI_RW_ATTR,\
|
||||
tricat(rmi_fn_, FNUM, _##propname##_show),\
|
||||
tricat(rmi_fn_, FNUM, _##propname##_store));
|
||||
|
||||
#define simple_show_union_struct(regtype, propname, fmt)\
|
||||
static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(struct device *dev,\
|
||||
struct device_attribute *attr, char *buf) {\
|
||||
struct rmi_function_container *fc;\
|
||||
struct FUNCTION_DATA *data;\
|
||||
\
|
||||
fc = to_rmi_function_container(dev);\
|
||||
data = fc->data;\
|
||||
\
|
||||
return snprintf(buf, PAGE_SIZE, fmt,\
|
||||
data->regtype.propname);\
|
||||
}
|
||||
|
||||
#define show_union_struct(regtype, reg_group, propname, fmt)\
|
||||
static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(\
|
||||
struct device *dev,\
|
||||
struct device_attribute *attr,\
|
||||
char *buf) {\
|
||||
struct rmi_function_container *fc;\
|
||||
struct FUNCTION_DATA *data;\
|
||||
int result;\
|
||||
\
|
||||
fc = to_rmi_function_container(dev);\
|
||||
data = fc->data;\
|
||||
\
|
||||
mutex_lock(&data->regtype##_mutex);\
|
||||
/* Read current regtype values */\
|
||||
result = rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
|
||||
(u8 *)data->regtype.reg_group,\
|
||||
sizeof(data->regtype.reg_group->regs));\
|
||||
mutex_unlock(&data->regtype##_mutex);\
|
||||
if (result < 0) {\
|
||||
dev_dbg(dev, "%s : Could not read regtype at 0x%x\\n",\
|
||||
__func__, data->regtype.reg_group->address);\
|
||||
return result;\
|
||||
}\
|
||||
return snprintf(buf, PAGE_SIZE, fmt,\
|
||||
data->regtype.reg_group->propname);\
|
||||
}\
|
||||
|
||||
#define show_store_union_struct(regtype, reg_group, propname, fmt)\
|
||||
show_union_struct(regtype, reg_group, propname, fmt)\
|
||||
\
|
||||
static ssize_t tricat(rmi_fn_, FNUM, _##propname##_store)(\
|
||||
struct device *dev,\
|
||||
struct device_attribute *attr,\
|
||||
const char *buf, size_t count) {\
|
||||
int result;\
|
||||
unsigned long val;\
|
||||
unsigned long old_val;\
|
||||
struct rmi_function_container *fc;\
|
||||
struct FUNCTION_DATA *data;\
|
||||
\
|
||||
fc = to_rmi_function_container(dev);\
|
||||
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;\
|
||||
/* Check value maybe */\
|
||||
\
|
||||
/* Read current regtype values */\
|
||||
mutex_lock(&data->regtype##_mutex);\
|
||||
result =\
|
||||
rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
|
||||
(u8 *)data->regtype.reg_group,\
|
||||
sizeof(data->regtype.reg_group->regs));\
|
||||
\
|
||||
if (result < 0) {\
|
||||
mutex_unlock(&data->regtype##_mutex);\
|
||||
dev_dbg(dev, "%s : Could not read regtype at 0x%x\\n",\
|
||||
__func__,\
|
||||
data->regtype.reg_group->address);\
|
||||
return result;\
|
||||
}\
|
||||
/* if the current regtype registers are already set as we want them,\
|
||||
* do nothing to them */\
|
||||
if (data->regtype.reg_group->propname == val) {\
|
||||
mutex_unlock(&data->regtype##_mutex);\
|
||||
return count;\
|
||||
}\
|
||||
/* Write the regtype back to the regtype register */\
|
||||
old_val = data->regtype.reg_group->propname;\
|
||||
data->regtype.reg_group->propname = val;\
|
||||
result =\
|
||||
rmi_write_block(fc->rmi_dev, data->regtype.reg_group->address,\
|
||||
(u8 *)data->regtype.reg_group,\
|
||||
sizeof(data->regtype.reg_group->regs));\
|
||||
if (result < 0) {\
|
||||
dev_dbg(dev, "%s : Could not write regtype to 0x%x\\n",\
|
||||
__func__,\
|
||||
data->regtype.reg_group->address);\
|
||||
/* revert change to local value if value not written */\
|
||||
data->regtype.reg_group->propname = old_val;\
|
||||
mutex_unlock(&data->regtype##_mutex);\
|
||||
return result;\
|
||||
}\
|
||||
mutex_unlock(&data->regtype##_mutex);\
|
||||
return count;\
|
||||
}
|
||||
|
||||
|
||||
#define show_repeated_union_struct(regtype, reg_group, propname, fmt)\
|
||||
static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(struct device *dev,\
|
||||
struct device_attribute *attr,\
|
||||
char *buf) {\
|
||||
struct rmi_function_container *fc;\
|
||||
struct FUNCTION_DATA *data;\
|
||||
int reg_length;\
|
||||
int result, size = 0;\
|
||||
char *temp;\
|
||||
int i;\
|
||||
\
|
||||
fc = to_rmi_function_container(dev);\
|
||||
data = fc->data;\
|
||||
mutex_lock(&data->regtype##_mutex);\
|
||||
\
|
||||
/* Read current regtype values */\
|
||||
reg_length = data->regtype.reg_group->length;\
|
||||
result = rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
|
||||
(u8*) data->regtype.reg_group->regs,\
|
||||
reg_length * sizeof(u8));\
|
||||
mutex_unlock(&data->regtype##_mutex);\
|
||||
if (result < 0) {\
|
||||
dev_dbg(dev, "%s : Could not read regtype at 0x%x\n"\
|
||||
"Data may be outdated.", __func__,\
|
||||
data->regtype.reg_group->address);\
|
||||
}\
|
||||
temp = buf;\
|
||||
for (i = 0; i < reg_length; i++) {\
|
||||
result = snprintf(temp, PAGE_SIZE - size, fmt " ",\
|
||||
data->regtype.reg_group->regs[i].propname);\
|
||||
if (result < 0) {\
|
||||
dev_err(dev, "%s : Could not write output.", __func__);\
|
||||
return result;\
|
||||
}\
|
||||
size += result;\
|
||||
temp += result;\
|
||||
}\
|
||||
result = snprintf(temp, PAGE_SIZE - size, "\n");\
|
||||
if (result < 0) {\
|
||||
dev_err(dev, "%s : Could not write output.", __func__);\
|
||||
return result;\
|
||||
}\
|
||||
return size + result;\
|
||||
}
|
||||
|
||||
#define show_store_repeated_union_struct(regtype, reg_group, propname, fmt)\
|
||||
show_repeated_union_struct(regtype, reg_group, propname, fmt)\
|
||||
\
|
||||
static ssize_t tricat(rmi_fn_, FNUM, _##propname##_store)(struct device *dev,\
|
||||
struct device_attribute *attr,\
|
||||
const char *buf, size_t count) {\
|
||||
struct rmi_function_container *fc;\
|
||||
struct FUNCTION_DATA *data;\
|
||||
int reg_length;\
|
||||
int result;\
|
||||
const char *temp;\
|
||||
int i;\
|
||||
unsigned int newval;\
|
||||
\
|
||||
fc = to_rmi_function_container(dev);\
|
||||
data = fc->data;\
|
||||
mutex_lock(&data->regtype##_mutex);\
|
||||
\
|
||||
/* Read current regtype values */\
|
||||
\
|
||||
reg_length = data->regtype.reg_group->length;\
|
||||
result = rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
|
||||
(u8*) data->regtype.reg_group->regs,\
|
||||
reg_length * sizeof(u8));\
|
||||
\
|
||||
if (result < 0) {\
|
||||
dev_dbg(dev, "%s : Could not read regtype at 0x%x\n"\
|
||||
"Data may be outdated.", __func__,\
|
||||
data->regtype.reg_group->address);\
|
||||
}\
|
||||
/* parse input */\
|
||||
\
|
||||
temp = buf;\
|
||||
for (i = 0; i < reg_length; i++) {\
|
||||
if(sscanf(temp, fmt, &newval) == 1) {\
|
||||
data->regtype.reg_group->regs[i].propname = newval;\
|
||||
} else {\
|
||||
/* If we don't read a value for each position, abort, restore
|
||||
* previous values locally by rereading */\
|
||||
result = rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
|
||||
(u8*) data->regtype.reg_group->regs,\
|
||||
reg_length * sizeof(u8));\
|
||||
\
|
||||
if (result < 0) {\
|
||||
dev_dbg(dev, "%s : Could not read regtype at 0x%x\n"\
|
||||
"Local data may be innacurrate.", __func__,\
|
||||
data->regtype.reg_group->address);\
|
||||
}\
|
||||
return -EINVAL;\
|
||||
}\
|
||||
/* move to next number */\
|
||||
while (*temp != 0) {\
|
||||
temp++;\
|
||||
if (isspace(*(temp - 1)) && !isspace(*temp))\
|
||||
break;\
|
||||
}\
|
||||
}\
|
||||
result = rmi_write_block(fc->rmi_dev, data->regtype.reg_group->address,\
|
||||
(u8*) data->regtype.reg_group->regs,\
|
||||
reg_length * sizeof(u8));\
|
||||
mutex_unlock(&data->regtype##_mutex);\
|
||||
if (result < 0) {\
|
||||
dev_dbg(dev, "%s : Could not write new values"\
|
||||
" to 0x%x\n", __func__, data->regtype.reg_group->address);\
|
||||
return result;\
|
||||
}\
|
||||
return count;\
|
||||
}
|
||||
|
||||
/* Create templates for given types */
|
||||
#define simple_show_union_struct_unsigned(regtype, propname)\
|
||||
simple_show_union_struct(regtype, propname, "%u\n")
|
||||
|
||||
#define show_union_struct_unsigned(regtype, reg_group, propname)\
|
||||
show_union_struct(regtype, reg_group, propname, "%u\n")
|
||||
|
||||
#define show_store_union_struct_unsigned(regtype, reg_group, propname)\
|
||||
show_store_union_struct(regtype, reg_group, propname, "%u\n")
|
||||
|
||||
#define show_repeated_union_struct_unsigned(regtype, reg_group, propname)\
|
||||
show_repeated_union_struct(regtype, reg_group, propname, "%u")
|
||||
|
||||
#define show_store_repeated_union_struct_unsigned(regtype, reg_group, propname)\
|
||||
show_store_repeated_union_struct(regtype, reg_group, propname, "%u")
|
||||
|
||||
/* Remove access to raw format string versions */
|
||||
/*#undef simple_show_union_struct
|
||||
#undef show_union_struct_unsigned
|
||||
#undef show_store_union_struct
|
||||
#undef show_repeated_union_struct
|
||||
#undef show_store_repeated_union_struct*/
|
||||
|
||||
#endif
|
||||
|
||||
#define GROUP(_attrs) { \
|
||||
.attrs = _attrs, \
|
||||
}
|
||||
|
||||
#define attrify(nm) &dev_attr_##nm.attr
|
||||
|
||||
union f01_device_status {
|
||||
struct {
|
||||
u8 status_code:4;
|
||||
u8 reserved:2;
|
||||
u8 flash_prog:1;
|
||||
u8 unconfigured:1;
|
||||
};
|
||||
u8 reg;
|
||||
};
|
||||
|
||||
struct rmi_driver_data {
|
||||
struct rmi_function_container rmi_functions;
|
||||
|
||||
struct rmi_function_container *f01_container;
|
||||
bool f01_bootloader_mode;
|
||||
|
||||
int num_of_irq_regs;
|
||||
int irq_count;
|
||||
u8 *current_irq_mask;
|
||||
u8 *irq_mask_store;
|
||||
bool irq_stored;
|
||||
struct mutex irq_mutex;
|
||||
struct mutex pdt_mutex;
|
||||
|
||||
unsigned char pdt_props;
|
||||
unsigned char bsr;
|
||||
bool enabled;
|
||||
|
||||
#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
|
||||
|
||||
#ifdef CONFIG_RMI4_DEBUG
|
||||
#ifdef CONFIG_RMI4_SPI
|
||||
struct dentry *debugfs_delay;
|
||||
#endif
|
||||
struct dentry *debugfs_phys;
|
||||
struct dentry *debugfs_reg_ctl;
|
||||
struct dentry *debugfs_reg;
|
||||
u16 reg_debug_addr;
|
||||
u8 reg_debug_size;
|
||||
#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;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
1389
drivers/input/touchscreen/rmi4/rmi_f01.c
Executable file
1389
drivers/input/touchscreen/rmi4/rmi_f01.c
Executable file
File diff suppressed because it is too large
Load Diff
771
drivers/input/touchscreen/rmi4/rmi_f09.c
Executable file
771
drivers/input/touchscreen/rmi4/rmi_f09.c
Executable file
File diff suppressed because it is too large
Load Diff
1947
drivers/input/touchscreen/rmi4/rmi_f11.c
Executable file
1947
drivers/input/touchscreen/rmi4/rmi_f11.c
Executable file
File diff suppressed because it is too large
Load Diff
731
drivers/input/touchscreen/rmi4/rmi_f17.c
Executable file
731
drivers/input/touchscreen/rmi4/rmi_f17.c
Executable file
File diff suppressed because it is too large
Load Diff
1505
drivers/input/touchscreen/rmi4/rmi_f19.c
Executable file
1505
drivers/input/touchscreen/rmi4/rmi_f19.c
Executable file
File diff suppressed because it is too large
Load Diff
1809
drivers/input/touchscreen/rmi4/rmi_f1a.c
Executable file
1809
drivers/input/touchscreen/rmi4/rmi_f1a.c
Executable file
File diff suppressed because it is too large
Load Diff
751
drivers/input/touchscreen/rmi4/rmi_f21.c
Executable file
751
drivers/input/touchscreen/rmi4/rmi_f21.c
Executable file
File diff suppressed because it is too large
Load Diff
962
drivers/input/touchscreen/rmi4/rmi_f34.c
Executable file
962
drivers/input/touchscreen/rmi4/rmi_f34.c
Executable file
File diff suppressed because it is too large
Load Diff
2272
drivers/input/touchscreen/rmi4/rmi_f54.c
Executable file
2272
drivers/input/touchscreen/rmi4/rmi_f54.c
Executable file
File diff suppressed because it is too large
Load Diff
453
drivers/input/touchscreen/rmi4/rmi_i2c.c
Executable file
453
drivers/input/touchscreen/rmi4/rmi_i2c.c
Executable file
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
* 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 COMMS_DEBUG 0
|
||||
|
||||
#define IRQ_DEBUG 0
|
||||
|
||||
#if COMMS_DEBUG || IRQ_DEBUG
|
||||
#define DEBUG
|
||||
#endif
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/rmi.h>
|
||||
#include "rmi_driver.h"
|
||||
#define RMI_PAGE_SELECT_REGISTER 0xff
|
||||
#define RMI_I2C_PAGE(addr) (((addr) >> 8) & 0xff)
|
||||
#ifndef CONFIG_RMI4_I2C_SCL_RATE
|
||||
#define CONFIG_RMI4_I2C_SCL_RATE 100000 // 100kHz
|
||||
#endif
|
||||
|
||||
|
||||
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(pdata->attn_gpio));
|
||||
#endif
|
||||
if (gpio_get_value(pdata->attn_gpio) == pdata->attn_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_normal_send(client, txbuf, sizeof(txbuf), CONFIG_RMI4_I2C_SCL_RATE);
|
||||
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;
|
||||
}
|
||||
|
||||
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_normal_send(client, txbuf, sizeof(txbuf), CONFIG_RMI4_I2C_SCL_RATE);
|
||||
if (retval < 0)
|
||||
phys->info.tx_errs++;
|
||||
else
|
||||
retval--; /* don't count the address byte */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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_normal_send(client, txbuf, sizeof(txbuf), CONFIG_RMI4_I2C_SCL_RATE);
|
||||
if (retval != sizeof(txbuf)) {
|
||||
phys->info.tx_errs++;
|
||||
retval = (retval < 0) ? retval : -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
retval = i2c_master_normal_recv(client, buf, len, CONFIG_RMI4_I2C_SCL_RATE);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void CompleteReflash(struct rmi_phys_device *phys);
|
||||
|
||||
static int __devinit 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;
|
||||
}
|
||||
pr_info("%s: Probing %s at %#02x (IRQ %d).\n", __func__,
|
||||
pdata->sensor_name ? pdata->sensor_name : "-no name-",
|
||||
client->addr, pdata->attn_gpio);
|
||||
|
||||
error = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
|
||||
if (!error) {
|
||||
dev_err(&client->dev, "i2c_check_functionality error %d.\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
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->attn_gpio);
|
||||
if (pdata->level_triggered) {
|
||||
data->irq_flags = IRQF_ONESHOT |
|
||||
((pdata->attn_polarity == RMI_ATTN_ACTIVE_HIGH) ?
|
||||
IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW);
|
||||
} else {
|
||||
data->irq_flags =
|
||||
(pdata->attn_polarity == RMI_ATTN_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(pdata->gpio_data, true);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev, "failed to setup irq %d\n",
|
||||
pdata->attn_gpio);
|
||||
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_gpio;
|
||||
}
|
||||
i2c_set_clientdata(client, rmi_phys);
|
||||
|
||||
if (pdata->attn_gpio > 0) {
|
||||
error = acquire_attn_irq(data);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev,
|
||||
"request_threaded_irq failed %d\n",
|
||||
pdata->attn_gpio);
|
||||
goto err_unregister;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_RMI4_DEV)
|
||||
error = gpio_export(pdata->attn_gpio, 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->attn_gpio);
|
||||
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->attn_gpio);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_RMI4_DEV */
|
||||
|
||||
dev_info(&client->dev, "registered rmi i2c driver at 0x%.2X.\n",
|
||||
client->addr);
|
||||
//reflash the new firmware revision hhb@rock-chips.com
|
||||
#ifdef CONFIG_RMI4_REFLASH_WHEN_BOOT
|
||||
CompleteReflash(rmi_phys);
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
err_unregister:
|
||||
rmi_unregister_phys_device(rmi_phys);
|
||||
err_gpio:
|
||||
if (pdata->gpio_config)
|
||||
pdata->gpio_config(pdata->gpio_data, false);
|
||||
err_data:
|
||||
kfree(data);
|
||||
err_phys:
|
||||
kfree(rmi_phys);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit 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;
|
||||
|
||||
disable_device(phys);
|
||||
rmi_unregister_phys_device(phys);
|
||||
kfree(phys->data);
|
||||
kfree(phys);
|
||||
|
||||
if (pd->gpio_config)
|
||||
pd->gpio_config(&pd->gpio_data, 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 = __devexit_p(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_init(rmi_i2c_init);
|
||||
module_exit(rmi_i2c_exit);
|
||||
|
||||
MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
|
||||
MODULE_DESCRIPTION("RMI I2C driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(RMI_DRIVER_VERSION);
|
||||
670
drivers/input/touchscreen/rmi4/rmi_reflash.c
Normal file
670
drivers/input/touchscreen/rmi4/rmi_reflash.c
Normal file
File diff suppressed because it is too large
Load Diff
2916
drivers/input/touchscreen/rmi4/rmi_reflash.h
Normal file
2916
drivers/input/touchscreen/rmi4/rmi_reflash.h
Normal file
File diff suppressed because it is too large
Load Diff
909
drivers/input/touchscreen/rmi4/rmi_spi.c
Executable file
909
drivers/input/touchscreen/rmi4/rmi_spi.c
Executable file
File diff suppressed because it is too large
Load Diff
609
include/linux/rmi.h
Executable file
609
include/linux/rmi.h
Executable file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user