Commit dac90dc2 authored by Lothar Waßmann's avatar Lothar Waßmann Committed by Dmitry Torokhov

Input: edt-ft5x06 - add DT support

Signed-off-by: default avatarLothar Waßmann <LW@KARO-electronics.de>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 1730d814
FocalTech EDT-FT5x06 Polytouch driver
=====================================
There are 3 variants of the chip for various touch panel sizes
FT5206GE1 2.8" .. 3.8"
FT5306DE4 4.3" .. 7"
FT5406EE8 7" .. 8.9"
The software interface is identical for all those chips, so that
currently there is no need for the driver to distinguish between the
different chips. Nevertheless distinct compatible strings are used so
that a distinction can be added if necessary without changing the DT
bindings.
Required properties:
- compatible: "edt,edt-ft5206"
or: "edt,edt-ft5306"
or: "edt,edt-ft5406"
- reg: I2C slave address of the chip (0x38)
- interrupt-parent: a phandle pointing to the interrupt controller
serving the interrupt for this chip
- interrupts: interrupt specification for the touchdetect
interrupt
Optional properties:
- reset-gpios: GPIO specification for the RESET input
- wake-gpios: GPIO specification for the WAKE input
- pinctrl-names: should be "default"
- pinctrl-0: a phandle pointing to the pin settings for the
control gpios
- threshold: allows setting the "click"-threshold in the range
from 20 to 80.
- gain: allows setting the sensitivity in the range from 0 to
31. Note that lower values indicate higher
sensitivity.
- offset: allows setting the edge compensation in the range from
0 to 31.
Example:
polytouch: edt-ft5x06@38 {
compatible = "edt,edt-ft5406", "edt,edt-ft5x06";
reg = <0x38>;
pinctrl-names = "default";
pinctrl-0 = <&edt_ft5x06_pins>;
interrupt-parent = <&gpio2>;
interrupts = <5 0>;
reset-gpios = <&gpio2 6 1>;
wake-gpios = <&gpio4 9 0>;
};
/* /*
* Copyright (C) 2012 Simon Budig, <simon.budig@kernelconcepts.de> * Copyright (C) 2012 Simon Budig, <simon.budig@kernelconcepts.de>
* Lothar Waßmann <LW@KARO-electronics.de> (DT support)
* *
* This software is licensed under the terms of the GNU General Public * This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and * License version 2, as published by the Free Software Foundation, and
...@@ -33,6 +34,7 @@ ...@@ -33,6 +34,7 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/input/mt.h> #include <linux/input/mt.h>
#include <linux/input/edt-ft5x06.h> #include <linux/input/edt-ft5x06.h>
...@@ -65,6 +67,10 @@ struct edt_ft5x06_ts_data { ...@@ -65,6 +67,10 @@ struct edt_ft5x06_ts_data {
u16 num_x; u16 num_x;
u16 num_y; u16 num_y;
int reset_pin;
int irq_pin;
int wake_pin;
#if defined(CONFIG_DEBUG_FS) #if defined(CONFIG_DEBUG_FS)
struct dentry *debug_dir; struct dentry *debug_dir;
u8 *raw_buffer; u8 *raw_buffer;
...@@ -614,24 +620,38 @@ edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) ...@@ -614,24 +620,38 @@ edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
#endif /* CONFIG_DEBUGFS */ #endif /* CONFIG_DEBUGFS */
static int edt_ft5x06_ts_reset(struct i2c_client *client, static int edt_ft5x06_ts_reset(struct i2c_client *client,
int reset_pin) struct edt_ft5x06_ts_data *tsdata)
{ {
int error; int error;
if (gpio_is_valid(reset_pin)) { if (gpio_is_valid(tsdata->wake_pin)) {
error = devm_gpio_request_one(&client->dev,
tsdata->wake_pin, GPIOF_OUT_INIT_LOW,
"edt-ft5x06 wake");
if (error) {
dev_err(&client->dev,
"Failed to request GPIO %d as wake pin, error %d\n",
tsdata->wake_pin, error);
return error;
}
mdelay(5);
gpio_set_value(tsdata->wake_pin, 1);
}
if (gpio_is_valid(tsdata->reset_pin)) {
/* this pulls reset down, enabling the low active reset */ /* this pulls reset down, enabling the low active reset */
error = devm_gpio_request_one(&client->dev, reset_pin, error = devm_gpio_request_one(&client->dev,
GPIOF_OUT_INIT_LOW, tsdata->reset_pin, GPIOF_OUT_INIT_LOW,
"edt-ft5x06 reset"); "edt-ft5x06 reset");
if (error) { if (error) {
dev_err(&client->dev, dev_err(&client->dev,
"Failed to request GPIO %d as reset pin, error %d\n", "Failed to request GPIO %d as reset pin, error %d\n",
reset_pin, error); tsdata->reset_pin, error);
return error; return error;
} }
mdelay(50); mdelay(50);
gpio_set_value(reset_pin, 1); gpio_set_value(tsdata->reset_pin, 1);
mdelay(100); mdelay(100);
} }
...@@ -672,6 +692,20 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client, ...@@ -672,6 +692,20 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client,
pdata->name <= edt_ft5x06_attr_##name.limit_high) \ pdata->name <= edt_ft5x06_attr_##name.limit_high) \
edt_ft5x06_register_write(tsdata, reg, pdata->name) edt_ft5x06_register_write(tsdata, reg, pdata->name)
#define EDT_GET_PROP(name, reg) { \
u32 val; \
if (of_property_read_u32(np, #name, &val) == 0) \
edt_ft5x06_register_write(tsdata, reg, val); \
}
static void edt_ft5x06_ts_get_dt_defaults(struct device_node *np,
struct edt_ft5x06_ts_data *tsdata)
{
EDT_GET_PROP(threshold, WORK_REGISTER_THRESHOLD);
EDT_GET_PROP(gain, WORK_REGISTER_GAIN);
EDT_GET_PROP(offset, WORK_REGISTER_OFFSET);
}
static void static void
edt_ft5x06_ts_get_defaults(struct edt_ft5x06_ts_data *tsdata, edt_ft5x06_ts_get_defaults(struct edt_ft5x06_ts_data *tsdata,
const struct edt_ft5x06_platform_data *pdata) const struct edt_ft5x06_platform_data *pdata)
...@@ -699,6 +733,30 @@ edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata) ...@@ -699,6 +733,30 @@ edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata)
tsdata->num_y = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_Y); tsdata->num_y = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_Y);
} }
#ifdef CONFIG_OF
static int edt_ft5x06_i2c_ts_probe_dt(struct device *dev,
struct edt_ft5x06_ts_data *tsdata)
{
struct device_node *np = dev->of_node;
/*
* irq_pin is not needed for DT setup.
* irq is associated via 'interrupts' property in DT
*/
tsdata->irq_pin = -EINVAL;
tsdata->reset_pin = of_get_named_gpio(np, "reset-gpios", 0);
tsdata->wake_pin = of_get_named_gpio(np, "wake-gpios", 0);
return 0;
}
#else
static inline int edt_ft5x06_i2c_ts_probe_dt(struct device *dev,
struct edt_ft5x06_ts_data *tsdata)
{
return -ENODEV;
}
#endif
static int edt_ft5x06_ts_probe(struct i2c_client *client, static int edt_ft5x06_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
...@@ -711,32 +769,40 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, ...@@ -711,32 +769,40 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n"); dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n");
tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL);
if (!tsdata) {
dev_err(&client->dev, "failed to allocate driver data.\n");
return -ENOMEM;
}
if (!pdata) { if (!pdata) {
dev_err(&client->dev, "no platform data?\n"); error = edt_ft5x06_i2c_ts_probe_dt(&client->dev, tsdata);
return -EINVAL; if (error) {
dev_err(&client->dev,
"DT probe failed and no platform data present\n");
return error;
}
} else {
tsdata->reset_pin = pdata->reset_pin;
tsdata->irq_pin = pdata->irq_pin;
tsdata->wake_pin = -EINVAL;
} }
error = edt_ft5x06_ts_reset(client, pdata->reset_pin); error = edt_ft5x06_ts_reset(client, tsdata);
if (error) if (error)
return error; return error;
if (gpio_is_valid(pdata->irq_pin)) { if (gpio_is_valid(tsdata->irq_pin)) {
error = devm_gpio_request_one(&client->dev, pdata->irq_pin, error = devm_gpio_request_one(&client->dev, tsdata->irq_pin,
GPIOF_IN, "edt-ft5x06 irq"); GPIOF_IN, "edt-ft5x06 irq");
if (error) { if (error) {
dev_err(&client->dev, dev_err(&client->dev,
"Failed to request GPIO %d, error %d\n", "Failed to request GPIO %d, error %d\n",
pdata->irq_pin, error); tsdata->irq_pin, error);
return error; return error;
} }
} }
tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL);
if (!tsdata) {
dev_err(&client->dev, "failed to allocate driver data.\n");
return -ENOMEM;
}
input = devm_input_allocate_device(&client->dev); input = devm_input_allocate_device(&client->dev);
if (!input) { if (!input) {
dev_err(&client->dev, "failed to allocate input device.\n"); dev_err(&client->dev, "failed to allocate input device.\n");
...@@ -754,7 +820,11 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, ...@@ -754,7 +820,11 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
return error; return error;
} }
if (!pdata)
edt_ft5x06_ts_get_dt_defaults(client->dev.of_node, tsdata);
else
edt_ft5x06_ts_get_defaults(tsdata, pdata); edt_ft5x06_ts_get_defaults(tsdata, pdata);
edt_ft5x06_ts_get_parameters(tsdata); edt_ft5x06_ts_get_parameters(tsdata);
dev_dbg(&client->dev, dev_dbg(&client->dev,
...@@ -784,8 +854,8 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, ...@@ -784,8 +854,8 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
input_set_drvdata(input, tsdata); input_set_drvdata(input, tsdata);
i2c_set_clientdata(client, tsdata); i2c_set_clientdata(client, tsdata);
error = devm_request_threaded_irq(&client->dev, client->irq, error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
NULL, edt_ft5x06_ts_isr, edt_ft5x06_ts_isr,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
client->name, tsdata); client->name, tsdata);
if (error) { if (error) {
...@@ -798,19 +868,21 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, ...@@ -798,19 +868,21 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
return error; return error;
error = input_register_device(input); error = input_register_device(input);
if (error) { if (error)
sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group); goto err_remove_attrs;
return error;
}
edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev)); edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev));
device_init_wakeup(&client->dev, 1); device_init_wakeup(&client->dev, 1);
dev_dbg(&client->dev, dev_dbg(&client->dev,
"EDT FT5x06 initialized: IRQ pin %d, Reset pin %d.\n", "EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n",
pdata->irq_pin, pdata->reset_pin); client->irq, tsdata->wake_pin, tsdata->reset_pin);
return 0; return 0;
err_remove_attrs:
sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group);
return error;
} }
static int edt_ft5x06_ts_remove(struct i2c_client *client) static int edt_ft5x06_ts_remove(struct i2c_client *client)
...@@ -854,10 +926,21 @@ static const struct i2c_device_id edt_ft5x06_ts_id[] = { ...@@ -854,10 +926,21 @@ static const struct i2c_device_id edt_ft5x06_ts_id[] = {
}; };
MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id); MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id);
#ifdef CONFIG_OF
static const struct of_device_id edt_ft5x06_of_match[] = {
{ .compatible = "edt,edt-ft5206", },
{ .compatible = "edt,edt-ft5306", },
{ .compatible = "edt,edt-ft5406", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match);
#endif
static struct i2c_driver edt_ft5x06_ts_driver = { static struct i2c_driver edt_ft5x06_ts_driver = {
.driver = { .driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "edt_ft5x06", .name = "edt_ft5x06",
.of_match_table = of_match_ptr(edt_ft5x06_of_match),
.pm = &edt_ft5x06_ts_pm_ops, .pm = &edt_ft5x06_ts_pm_ops,
}, },
.id_table = edt_ft5x06_ts_id, .id_table = edt_ft5x06_ts_id,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment