From 07f9e5cf8e4154ad17b92ad288be0f04fa0cb94f Mon Sep 17 00:00:00 2001 From: Denis Carikli Date: Tue, 19 Nov 2013 11:56:04 -0800 Subject: [PATCH] Input: tsc2007 - add device tree support. Signed-off-by: Denis Carikli Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/tsc2007.txt | 41 +++++ arch/arm/mach-imx/mach-cpuimx35.c | 2 +- arch/arm/mach-imx/mach-cpuimx51sd.c | 2 +- arch/sh/boards/mach-ecovec24/setup.c | 2 +- drivers/input/touchscreen/tsc2007.c | 173 ++++++++++++++---- include/linux/i2c/tsc2007.h | 6 +- 6 files changed, 180 insertions(+), 46 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/tsc2007.txt diff --git a/Documentation/devicetree/bindings/input/touchscreen/tsc2007.txt b/Documentation/devicetree/bindings/input/touchscreen/tsc2007.txt new file mode 100644 index 00000000000..ec365e17223 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/tsc2007.txt @@ -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>; + }; + + /* ... */ + }; diff --git a/arch/arm/mach-imx/mach-cpuimx35.c b/arch/arm/mach-imx/mach-cpuimx35.c index 771362d1fbe..65e4c53e155 100644 --- a/arch/arm/mach-imx/mach-cpuimx35.c +++ b/arch/arm/mach-imx/mach-cpuimx35.c @@ -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); } diff --git a/arch/arm/mach-imx/mach-cpuimx51sd.c b/arch/arm/mach-imx/mach-cpuimx51sd.c index 9b5ddf5bbd3..1fba2b8e983 100644 --- a/arch/arm/mach-imx/mach-cpuimx51sd.c +++ b/arch/arm/mach-imx/mach-cpuimx51sd.c @@ -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); diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index 1fa8be40977..23d7e45f9d1 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -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); diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c index 0b67ba476b4..88b150a0b8e 100644 --- a/drivers/input/touchscreen/tsc2007.c +++ b/drivers/input/touchscreen/tsc2007.c @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #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); + + 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 (!pdata) { - dev_err(&client->dev, "platform data is required!\n"); + 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) { - struct tsc2007 *ts = i2c_get_clientdata(client); - struct tsc2007_platform_data *pdata = client->dev.platform_data; + const struct tsc2007_platform_data *pdata = dev_get_platdata(&client->dev); + struct tsc2007 *ts = i2c_get_clientdata(client); 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, diff --git a/include/linux/i2c/tsc2007.h b/include/linux/i2c/tsc2007.h index 506a9f7af51..041c8e82366 100644 --- a/include/linux/i2c/tsc2007.h +++ b/include/linux/i2c/tsc2007.h @@ -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); }; -- 2.41.0