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:
Denis Carikli 2013-11-19 11:56:04 -08:00 коммит произвёл Dmitry Torokhov
Родитель bd77c32194
Коммит 07f9e5cf8e
6 изменённых файлов: 180 добавлений и 46 удалений

Просмотреть файл

@ -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) #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); return !gpio_get_value(TSC2007_IRQGPIO);
} }

Просмотреть файл

@ -121,7 +121,7 @@ static const struct imxuart_platform_data uart_pdata __initconst = {
.flags = IMXUART_HAVE_RTSCTS, .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) if (mx51_revision() < IMX_CHIP_REVISION_3_0)
return !gpio_get_value(TSC2007_IRQGPIO_REV2); return !gpio_get_value(TSC2007_IRQGPIO_REV2);

Просмотреть файл

@ -501,7 +501,7 @@ static struct platform_device keysc_device = {
/* TouchScreen */ /* TouchScreen */
#define IRQ0 evt2irq(0x600) #define IRQ0 evt2irq(0x600)
static int ts_get_pendown_state(void) static int ts_get_pendown_state(struct device *dev)
{ {
int val = 0; int val = 0;
gpio_free(GPIO_FN_INTC_IRQ0); gpio_free(GPIO_FN_INTC_IRQ0);

Просмотреть файл

@ -26,6 +26,9 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/i2c/tsc2007.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_TEMP0 (0x0 << 4)
#define TSC2007_MEASURE_AUX (0x2 << 4) #define TSC2007_MEASURE_AUX (0x2 << 4)
@ -74,13 +77,17 @@ struct tsc2007 {
u16 max_rt; u16 max_rt;
unsigned long poll_delay; unsigned long poll_delay;
unsigned long poll_period; unsigned long poll_period;
int fuzzx;
int fuzzy;
int fuzzz;
unsigned gpio;
int irq; int irq;
wait_queue_head_t wait; wait_queue_head_t wait;
bool stopped; bool stopped;
int (*get_pendown_state)(void); int (*get_pendown_state)(struct device *);
void (*clear_penirq)(void); void (*clear_penirq)(void);
}; };
@ -161,7 +168,7 @@ static bool tsc2007_is_pen_down(struct tsc2007 *ts)
if (!ts->get_pendown_state) if (!ts->get_pendown_state)
return true; 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) 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); 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 * If pressure reported is 0 and we don't have
* callback to check pendown state, we have to * 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; struct tsc2007 *ts = handle;
if (!ts->get_pendown_state || likely(ts->get_pendown_state())) if (tsc2007_is_pen_down(ts))
return IRQ_WAKE_THREAD; return IRQ_WAKE_THREAD;
if (ts->clear_penirq) if (ts->clear_penirq)
@ -273,35 +280,74 @@ static void tsc2007_close(struct input_dev *input_dev)
tsc2007_stop(ts); tsc2007_stop(ts);
} }
static int tsc2007_probe(struct i2c_client *client, #ifdef CONFIG_OF
const struct i2c_device_id *id) static int tsc2007_get_pendown_state_gpio(struct device *dev)
{ {
struct tsc2007 *ts; struct i2c_client *client = to_i2c_client(dev);
struct tsc2007_platform_data *pdata = client->dev.platform_data; struct tsc2007 *ts = i2c_get_clientdata(client);
struct input_dev *input_dev;
int err;
if (!pdata) { return !gpio_get_value(ts->gpio);
dev_err(&client->dev, "platform data is required!\n"); }
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; return -EINVAL;
} }
if (!i2c_check_functionality(client->adapter, if (!of_property_read_u32(np, "ti,max-rt", &val32))
I2C_FUNC_SMBUS_READ_WORD_DATA)) ts->max_rt = val32;
return -EIO; else
ts->max_rt = MAX_12BIT;
ts = kzalloc(sizeof(struct tsc2007), GFP_KERNEL); if (!of_property_read_u32(np, "ti,fuzzx", &val32))
input_dev = input_allocate_device(); ts->fuzzx = val32;
if (!ts || !input_dev) {
err = -ENOMEM; if (!of_property_read_u32(np, "ti,fuzzy", &val32))
goto err_free_mem; 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->gpio = of_get_gpio(np, 0);
ts->irq = client->irq; if (gpio_is_valid(ts->gpio))
ts->input = input_dev; ts->get_pendown_state = tsc2007_get_pendown_state_gpio;
init_waitqueue_head(&ts->wait); 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->model = pdata->model;
ts->x_plate_ohms = pdata->x_plate_ohms; ts->x_plate_ohms = pdata->x_plate_ohms;
ts->max_rt = pdata->max_rt ? : MAX_12BIT; 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->poll_period = pdata->poll_period ? : 1;
ts->get_pendown_state = pdata->get_pendown_state; ts->get_pendown_state = pdata->get_pendown_state;
ts->clear_penirq = pdata->clear_penirq; ts->clear_penirq = pdata->clear_penirq;
ts->fuzzx = pdata->fuzzx;
ts->fuzzy = pdata->fuzzy;
ts->fuzzz = pdata->fuzzz;
if (pdata->x_plate_ohms == 0) { if (pdata->x_plate_ohms == 0) {
dev_err(&client->dev, "x_plate_ohms is not set up in platform data"); dev_err(&client->dev, "x_plate_ohms is not set up in platform data");
err = -EINVAL; return -EINVAL;
goto err_free_mem;
} }
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), snprintf(ts->phys, sizeof(ts->phys),
"%s/input0", dev_name(&client->dev)); "%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->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 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_X, 0, MAX_12BIT, ts->fuzzx, 0);
input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, pdata->fuzzy, 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, 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(); pdata->init_platform_hw();
err = request_threaded_irq(ts->irq, tsc2007_hard_irq, tsc2007_soft_irq, err = request_threaded_irq(ts->irq, tsc2007_hard_irq, tsc2007_soft_irq,
IRQF_ONESHOT, client->dev.driver->name, ts); IRQF_ONESHOT, client->dev.driver->name, ts);
if (err < 0) { if (err < 0) {
dev_err(&client->dev, "irq %d busy?\n", ts->irq); dev_err(&client->dev, "irq %d busy?\n", ts->irq);
goto err_free_mem; goto err_free_input;
} }
tsc2007_stop(ts); tsc2007_stop(ts);
@ -352,28 +439,25 @@ static int tsc2007_probe(struct i2c_client *client,
if (err) if (err)
goto err_free_irq; goto err_free_irq;
i2c_set_clientdata(client, ts);
return 0; return 0;
err_free_irq: err_free_irq:
free_irq(ts->irq, ts); free_irq(ts->irq, ts);
if (pdata->exit_platform_hw) if (pdata && pdata->exit_platform_hw)
pdata->exit_platform_hw(); pdata->exit_platform_hw();
err_free_mem: err_free_input:
input_free_device(input_dev); input_free_device(input_dev);
kfree(ts);
return err; return err;
} }
static int tsc2007_remove(struct i2c_client *client) 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 *ts = i2c_get_clientdata(client);
struct tsc2007_platform_data *pdata = client->dev.platform_data;
free_irq(ts->irq, ts); free_irq(ts->irq, ts);
if (pdata->exit_platform_hw) if (pdata && pdata->exit_platform_hw)
pdata->exit_platform_hw(); pdata->exit_platform_hw();
input_unregister_device(ts->input); input_unregister_device(ts->input);
@ -389,10 +473,19 @@ static const struct i2c_device_id tsc2007_idtable[] = {
MODULE_DEVICE_TABLE(i2c, 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 = { static struct i2c_driver tsc2007_driver = {
.driver = { .driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "tsc2007" .name = "tsc2007",
.of_match_table = of_match_ptr(tsc2007_of_match),
}, },
.id_table = tsc2007_idtable, .id_table = tsc2007_idtable,
.probe = tsc2007_probe, .probe = tsc2007_probe,

Просмотреть файл

@ -14,9 +14,9 @@ struct tsc2007_platform_data {
int fuzzy; int fuzzy;
int fuzzz; int fuzzz;
int (*get_pendown_state)(void); int (*get_pendown_state)(struct device *);
void (*clear_penirq)(void); /* If needed, clear 2nd level /* If needed, clear 2nd level interrupt source */
interrupt source */ void (*clear_penirq)(void);
int (*init_platform_hw)(void); int (*init_platform_hw)(void);
void (*exit_platform_hw)(void); void (*exit_platform_hw)(void);
}; };