Input: tsc2007 - add device tree support.
Signed-off-by: Denis Carikli <denis@eukrea.com> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
This commit is contained in:
Родитель
bd77c32194
Коммит
07f9e5cf8e
|
@ -0,0 +1,41 @@
|
|||
* Texas Instruments tsc2007 touchscreen controller
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "ti,tsc2007".
|
||||
- reg: I2C address of the chip.
|
||||
- ti,x-plate-ohms: X-plate resistance in ohms.
|
||||
|
||||
Optional properties:
|
||||
- gpios: the interrupt gpio the chip is connected to (trough the penirq pin).
|
||||
The penirq pin goes to low when the panel is touched.
|
||||
(see GPIO binding[1] for more details).
|
||||
- interrupt-parent: the phandle for the gpio controller
|
||||
(see interrupt binding[0]).
|
||||
- interrupts: (gpio) interrupt to which the chip is connected
|
||||
(see interrupt binding[0]).
|
||||
- ti,max-rt: maximum pressure.
|
||||
- ti,fuzzx: specifies the absolute input fuzz x value.
|
||||
If set, it will permit noise in the data up to +- the value given to the fuzz
|
||||
parameter, that is used to filter noise from the event stream.
|
||||
- ti,fuzzy: specifies the absolute input fuzz y value.
|
||||
- ti,fuzzz: specifies the absolute input fuzz z value.
|
||||
- ti,poll-period: how much time to wait (in milliseconds) before reading again the
|
||||
values from the tsc2007.
|
||||
|
||||
[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
[1]: Documentation/devicetree/bindings/gpio/gpio.txt
|
||||
|
||||
Example:
|
||||
&i2c1 {
|
||||
/* ... */
|
||||
tsc2007@49 {
|
||||
compatible = "ti,tsc2007";
|
||||
reg = <0x49>;
|
||||
interrupt-parent = <&gpio4>;
|
||||
interrupts = <0x0 0x8>;
|
||||
gpios = <&gpio4 0 0>;
|
||||
ti,x-plate-ohms = <180>;
|
||||
};
|
||||
|
||||
/* ... */
|
||||
};
|
|
@ -53,7 +53,7 @@ static const struct imxi2c_platform_data
|
|||
};
|
||||
|
||||
#define TSC2007_IRQGPIO IMX_GPIO_NR(3, 2)
|
||||
static int tsc2007_get_pendown_state(void)
|
||||
static int tsc2007_get_pendown_state(struct device *dev)
|
||||
{
|
||||
return !gpio_get_value(TSC2007_IRQGPIO);
|
||||
}
|
||||
|
|
|
@ -121,7 +121,7 @@ static const struct imxuart_platform_data uart_pdata __initconst = {
|
|||
.flags = IMXUART_HAVE_RTSCTS,
|
||||
};
|
||||
|
||||
static int tsc2007_get_pendown_state(void)
|
||||
static int tsc2007_get_pendown_state(struct device *dev)
|
||||
{
|
||||
if (mx51_revision() < IMX_CHIP_REVISION_3_0)
|
||||
return !gpio_get_value(TSC2007_IRQGPIO_REV2);
|
||||
|
|
|
@ -501,7 +501,7 @@ static struct platform_device keysc_device = {
|
|||
/* TouchScreen */
|
||||
#define IRQ0 evt2irq(0x600)
|
||||
|
||||
static int ts_get_pendown_state(void)
|
||||
static int ts_get_pendown_state(struct device *dev)
|
||||
{
|
||||
int val = 0;
|
||||
gpio_free(GPIO_FN_INTC_IRQ0);
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/tsc2007.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
|
||||
#define TSC2007_MEASURE_AUX (0x2 << 4)
|
||||
|
@ -74,13 +77,17 @@ struct tsc2007 {
|
|||
u16 max_rt;
|
||||
unsigned long poll_delay;
|
||||
unsigned long poll_period;
|
||||
int fuzzx;
|
||||
int fuzzy;
|
||||
int fuzzz;
|
||||
|
||||
unsigned gpio;
|
||||
int irq;
|
||||
|
||||
wait_queue_head_t wait;
|
||||
bool stopped;
|
||||
|
||||
int (*get_pendown_state)(void);
|
||||
int (*get_pendown_state)(struct device *);
|
||||
void (*clear_penirq)(void);
|
||||
};
|
||||
|
||||
|
@ -161,7 +168,7 @@ static bool tsc2007_is_pen_down(struct tsc2007 *ts)
|
|||
if (!ts->get_pendown_state)
|
||||
return true;
|
||||
|
||||
return ts->get_pendown_state();
|
||||
return ts->get_pendown_state(&ts->client->dev);
|
||||
}
|
||||
|
||||
static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
|
||||
|
@ -178,7 +185,7 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
|
|||
|
||||
rt = tsc2007_calculate_pressure(ts, &tc);
|
||||
|
||||
if (rt == 0 && !ts->get_pendown_state) {
|
||||
if (!rt && !ts->get_pendown_state) {
|
||||
/*
|
||||
* If pressure reported is 0 and we don't have
|
||||
* callback to check pendown state, we have to
|
||||
|
@ -228,7 +235,7 @@ static irqreturn_t tsc2007_hard_irq(int irq, void *handle)
|
|||
{
|
||||
struct tsc2007 *ts = handle;
|
||||
|
||||
if (!ts->get_pendown_state || likely(ts->get_pendown_state()))
|
||||
if (tsc2007_is_pen_down(ts))
|
||||
return IRQ_WAKE_THREAD;
|
||||
|
||||
if (ts->clear_penirq)
|
||||
|
@ -273,35 +280,74 @@ static void tsc2007_close(struct input_dev *input_dev)
|
|||
tsc2007_stop(ts);
|
||||
}
|
||||
|
||||
static int tsc2007_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
#ifdef CONFIG_OF
|
||||
static int tsc2007_get_pendown_state_gpio(struct device *dev)
|
||||
{
|
||||
struct tsc2007 *ts;
|
||||
struct tsc2007_platform_data *pdata = client->dev.platform_data;
|
||||
struct input_dev *input_dev;
|
||||
int err;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct tsc2007 *ts = i2c_get_clientdata(client);
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&client->dev, "platform data is required!\n");
|
||||
return !gpio_get_value(ts->gpio);
|
||||
}
|
||||
|
||||
static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
|
||||
{
|
||||
struct device_node *np = client->dev.of_node;
|
||||
u32 val32;
|
||||
u64 val64;
|
||||
|
||||
if (!np) {
|
||||
dev_err(&client->dev, "missing device tree data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_WORD_DATA))
|
||||
return -EIO;
|
||||
if (!of_property_read_u32(np, "ti,max-rt", &val32))
|
||||
ts->max_rt = val32;
|
||||
else
|
||||
ts->max_rt = MAX_12BIT;
|
||||
|
||||
ts = kzalloc(sizeof(struct tsc2007), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!ts || !input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
if (!of_property_read_u32(np, "ti,fuzzx", &val32))
|
||||
ts->fuzzx = val32;
|
||||
|
||||
if (!of_property_read_u32(np, "ti,fuzzy", &val32))
|
||||
ts->fuzzy = val32;
|
||||
|
||||
if (!of_property_read_u32(np, "ti,fuzzz", &val32))
|
||||
ts->fuzzz = val32;
|
||||
|
||||
if (!of_property_read_u64(np, "ti,poll-period", &val64))
|
||||
ts->poll_period = val64;
|
||||
else
|
||||
ts->poll_period = 1;
|
||||
|
||||
if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32)) {
|
||||
ts->x_plate_ohms = val32;
|
||||
} else {
|
||||
dev_err(&client->dev, "missing ti,x-plate-ohms devicetree property.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ts->client = client;
|
||||
ts->irq = client->irq;
|
||||
ts->input = input_dev;
|
||||
init_waitqueue_head(&ts->wait);
|
||||
ts->gpio = of_get_gpio(np, 0);
|
||||
if (gpio_is_valid(ts->gpio))
|
||||
ts->get_pendown_state = tsc2007_get_pendown_state_gpio;
|
||||
else
|
||||
dev_warn(&client->dev,
|
||||
"GPIO not specified in DT (of_get_gpio returned %d)\n",
|
||||
ts->gpio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
|
||||
{
|
||||
dev_err(&client->dev, "platform data is required!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007 *ts,
|
||||
const struct tsc2007_platform_data *pdata,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
ts->model = pdata->model;
|
||||
ts->x_plate_ohms = pdata->x_plate_ohms;
|
||||
ts->max_rt = pdata->max_rt ? : MAX_12BIT;
|
||||
|
@ -309,13 +355,54 @@ static int tsc2007_probe(struct i2c_client *client,
|
|||
ts->poll_period = pdata->poll_period ? : 1;
|
||||
ts->get_pendown_state = pdata->get_pendown_state;
|
||||
ts->clear_penirq = pdata->clear_penirq;
|
||||
ts->fuzzx = pdata->fuzzx;
|
||||
ts->fuzzy = pdata->fuzzy;
|
||||
ts->fuzzz = pdata->fuzzz;
|
||||
|
||||
if (pdata->x_plate_ohms == 0) {
|
||||
dev_err(&client->dev, "x_plate_ohms is not set up in platform data");
|
||||
err = -EINVAL;
|
||||
goto err_free_mem;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tsc2007_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
const struct tsc2007_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
struct tsc2007 *ts;
|
||||
struct input_dev *input_dev;
|
||||
int err;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_WORD_DATA))
|
||||
return -EIO;
|
||||
|
||||
ts = devm_kzalloc(&client->dev, sizeof(struct tsc2007), GFP_KERNEL);
|
||||
if (!ts)
|
||||
return -ENOMEM;
|
||||
|
||||
if (pdata)
|
||||
err = tsc2007_probe_pdev(client, ts, pdata, id);
|
||||
else
|
||||
err = tsc2007_probe_dt(client, ts);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
input_dev = input_allocate_device();
|
||||
if (!input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto err_free_input;
|
||||
};
|
||||
|
||||
i2c_set_clientdata(client, ts);
|
||||
|
||||
ts->client = client;
|
||||
ts->irq = client->irq;
|
||||
ts->input = input_dev;
|
||||
init_waitqueue_head(&ts->wait);
|
||||
|
||||
snprintf(ts->phys, sizeof(ts->phys),
|
||||
"%s/input0", dev_name(&client->dev));
|
||||
|
||||
|
@ -331,19 +418,19 @@ static int tsc2007_probe(struct i2c_client *client,
|
|||
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, pdata->fuzzx, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, pdata->fuzzy, 0);
|
||||
input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, ts->fuzzx, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, ts->fuzzy, 0);
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT,
|
||||
pdata->fuzzz, 0);
|
||||
ts->fuzzz, 0);
|
||||
|
||||
if (pdata->init_platform_hw)
|
||||
if (pdata && pdata->init_platform_hw)
|
||||
pdata->init_platform_hw();
|
||||
|
||||
err = request_threaded_irq(ts->irq, tsc2007_hard_irq, tsc2007_soft_irq,
|
||||
IRQF_ONESHOT, client->dev.driver->name, ts);
|
||||
if (err < 0) {
|
||||
dev_err(&client->dev, "irq %d busy?\n", ts->irq);
|
||||
goto err_free_mem;
|
||||
goto err_free_input;
|
||||
}
|
||||
|
||||
tsc2007_stop(ts);
|
||||
|
@ -352,28 +439,25 @@ static int tsc2007_probe(struct i2c_client *client,
|
|||
if (err)
|
||||
goto err_free_irq;
|
||||
|
||||
i2c_set_clientdata(client, ts);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(ts->irq, ts);
|
||||
if (pdata->exit_platform_hw)
|
||||
if (pdata && pdata->exit_platform_hw)
|
||||
pdata->exit_platform_hw();
|
||||
err_free_mem:
|
||||
err_free_input:
|
||||
input_free_device(input_dev);
|
||||
kfree(ts);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tsc2007_remove(struct i2c_client *client)
|
||||
{
|
||||
const struct tsc2007_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
struct tsc2007 *ts = i2c_get_clientdata(client);
|
||||
struct tsc2007_platform_data *pdata = client->dev.platform_data;
|
||||
|
||||
free_irq(ts->irq, ts);
|
||||
|
||||
if (pdata->exit_platform_hw)
|
||||
if (pdata && pdata->exit_platform_hw)
|
||||
pdata->exit_platform_hw();
|
||||
|
||||
input_unregister_device(ts->input);
|
||||
|
@ -389,10 +473,19 @@ static const struct i2c_device_id tsc2007_idtable[] = {
|
|||
|
||||
MODULE_DEVICE_TABLE(i2c, tsc2007_idtable);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id tsc2007_of_match[] = {
|
||||
{ .compatible = "ti,tsc2007" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tsc2007_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver tsc2007_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "tsc2007"
|
||||
.name = "tsc2007",
|
||||
.of_match_table = of_match_ptr(tsc2007_of_match),
|
||||
},
|
||||
.id_table = tsc2007_idtable,
|
||||
.probe = tsc2007_probe,
|
||||
|
|
|
@ -14,9 +14,9 @@ struct tsc2007_platform_data {
|
|||
int fuzzy;
|
||||
int fuzzz;
|
||||
|
||||
int (*get_pendown_state)(void);
|
||||
void (*clear_penirq)(void); /* If needed, clear 2nd level
|
||||
interrupt source */
|
||||
int (*get_pendown_state)(struct device *);
|
||||
/* If needed, clear 2nd level interrupt source */
|
||||
void (*clear_penirq)(void);
|
||||
int (*init_platform_hw)(void);
|
||||
void (*exit_platform_hw)(void);
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче