Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fa6e3ca2 authored by Stefan Agner's avatar Stefan Agner Committed by Dmitry Torokhov
Browse files

Input: ad7879 - add device tree support



Add device tree support for the I2C and SPI variant of AD7879(-1).
This allows to specify the touchscreen controller as a I2C client
node or SPI slave device. Most of the options available in platform
data are also available as device tree properties, the only exception
being GPIO capabilities, which can not be activated through device
tree currently.

Signed-off-by: default avatarStefan Agner <stefan@agner.ch>
Acked-by: default avatarRob Herring <robh@kernel.org>
Acked-by: default avatarMichael Hennerich <michael.hennerich@analog.com>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 5f2940c4
Loading
Loading
Loading
Loading
+53 −0
Original line number Original line Diff line number Diff line
* Analog Devices AD7879(-1)/AD7889(-1) touchscreen interface (SPI/I2C)

Required properties:
- compatible			: for SPI slave, use "adi,ad7879"
				  for I2C slave, use "adi,ad7879-1"
- reg				: SPI chipselect/I2C slave address
				  See spi-bus.txt for more SPI slave properties
- interrupt-parent		: the phandle for the interrupt controller
- interrupts			: touch controller interrupt
- touchscreen-max-pressure	: maximum reported pressure
- adi,resistance-plate-x	: total resistance of X-plate (for pressure
				  calculation)
Optional properties:
- touchscreen-swapped-x-y	: X and Y axis are swapped (boolean)
- adi,first-conversion-delay	: 0-12: In 128us steps (starting with 128us)
				  13  : 2.560ms
				  14  : 3.584ms
				  15  : 4.096ms
				  This property has to be a '/bits/ 8' value
- adi,acquisition-time		: 0: 2us
				  1: 4us
				  2: 8us
				  3: 16us
				  This property has to be a '/bits/ 8' value
- adi,median-filter-size	: 0: disabled
				  1: 4 measurements
				  2: 8 measurements
				  3: 16 measurements
				  This property has to be a '/bits/ 8' value
- adi,averaging			: 0: 2 middle values (1 if median disabled)
				  1: 4 middle values
				  2: 8 middle values
				  3: 16 values
				  This property has to be a '/bits/ 8' value
- adi,conversion-interval:	: 0    : convert one time only
				  1-255: 515us + val * 35us (up to 9.440ms)
				  This property has to be a '/bits/ 8' value

Example:

	ad7879@2c {
		compatible = "adi,ad7879-1";
		reg = <0x2c>;
		interrupt-parent = <&gpio1>;
		interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
		touchscreen-max-pressure = <4096>;
		adi,resistance-plate-x = <120>;
		adi,first-conversion-delay = /bits/ 8 <3>;
		adi,acquisition-time = /bits/ 8 <1>;
		adi,median-filter-size = /bits/ 8 <2>;
		adi,averaging = /bits/ 8 <1>;
		adi,conversion-interval = /bits/ 8 <255>;
	};
+10 −0
Original line number Original line Diff line number Diff line
@@ -10,6 +10,7 @@
#include <linux/i2c.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/types.h>
#include <linux/of.h>
#include <linux/pm.h>
#include <linux/pm.h>


#include "ad7879.h"
#include "ad7879.h"
@@ -91,10 +92,19 @@ static const struct i2c_device_id ad7879_id[] = {
};
};
MODULE_DEVICE_TABLE(i2c, ad7879_id);
MODULE_DEVICE_TABLE(i2c, ad7879_id);


#ifdef CONFIG_OF
static const struct of_device_id ad7879_i2c_dt_ids[] = {
	{ .compatible = "adi,ad7879-1", },
	{ }
};
MODULE_DEVICE_TABLE(of, ad7879_i2c_dt_ids);
#endif

static struct i2c_driver ad7879_i2c_driver = {
static struct i2c_driver ad7879_i2c_driver = {
	.driver = {
	.driver = {
		.name	= "ad7879",
		.name	= "ad7879",
		.pm	= &ad7879_pm_ops,
		.pm	= &ad7879_pm_ops,
		.of_match_table = of_match_ptr(ad7879_i2c_dt_ids),
	},
	},
	.probe		= ad7879_i2c_probe,
	.probe		= ad7879_i2c_probe,
	.remove		= ad7879_i2c_remove,
	.remove		= ad7879_i2c_remove,
+10 −0
Original line number Original line Diff line number Diff line
@@ -10,6 +10,7 @@
#include <linux/pm.h>
#include <linux/pm.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/module.h>
#include <linux/of.h>


#include "ad7879.h"
#include "ad7879.h"


@@ -146,10 +147,19 @@ static int ad7879_spi_remove(struct spi_device *spi)
	return 0;
	return 0;
}
}


#ifdef CONFIG_OF
static const struct of_device_id ad7879_spi_dt_ids[] = {
	{ .compatible = "adi,ad7879", },
	{ }
};
MODULE_DEVICE_TABLE(of, ad7879_spi_dt_ids);
#endif

static struct spi_driver ad7879_spi_driver = {
static struct spi_driver ad7879_spi_driver = {
	.driver = {
	.driver = {
		.name	= "ad7879",
		.name	= "ad7879",
		.pm	= &ad7879_pm_ops,
		.pm	= &ad7879_pm_ops,
		.of_match_table = of_match_ptr(ad7879_spi_dt_ids),
	},
	},
	.probe		= ad7879_spi_probe,
	.probe		= ad7879_spi_probe,
	.remove		= ad7879_spi_remove,
	.remove		= ad7879_spi_remove,
+90 −58
Original line number Original line Diff line number Diff line
@@ -31,6 +31,7 @@
#include <linux/i2c.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/gpio.h>


#include <linux/input/touchscreen.h>
#include <linux/platform_data/ad7879.h>
#include <linux/platform_data/ad7879.h>
#include <linux/module.h>
#include <linux/module.h>
#include "ad7879.h"
#include "ad7879.h"
@@ -126,7 +127,6 @@ struct ad7879 {
	u8			pen_down_acc_interval;
	u8			pen_down_acc_interval;
	u8			median;
	u8			median;
	u16			x_plate_ohms;
	u16			x_plate_ohms;
	u16			pressure_max;
	u16			cmd_crtl1;
	u16			cmd_crtl1;
	u16			cmd_crtl2;
	u16			cmd_crtl2;
	u16			cmd_crtl3;
	u16			cmd_crtl3;
@@ -186,7 +186,7 @@ static int ad7879_report(struct ad7879 *ts)
		 * Sample found inconsistent, pressure is beyond
		 * Sample found inconsistent, pressure is beyond
		 * the maximum. Don't report it to user space.
		 * the maximum. Don't report it to user space.
		 */
		 */
		if (Rt > ts->pressure_max)
		if (Rt > input_abs_get_max(input_dev, ABS_PRESSURE))
			return -EINVAL;
			return -EINVAL;


		/*
		/*
@@ -469,7 +469,7 @@ static void ad7879_gpio_remove(struct ad7879 *ts)
{
{
	const struct ad7879_platform_data *pdata = dev_get_platdata(ts->dev);
	const struct ad7879_platform_data *pdata = dev_get_platdata(ts->dev);


	if (pdata->gpio_export)
	if (pdata && pdata->gpio_export)
		gpiochip_remove(&ts->gc);
		gpiochip_remove(&ts->gc);


}
}
@@ -485,6 +485,32 @@ static inline void ad7879_gpio_remove(struct ad7879 *ts)
}
}
#endif
#endif


static int ad7879_parse_dt(struct device *dev, struct ad7879 *ts)
{
	int err;
	u32 tmp;

	err = device_property_read_u32(dev, "adi,resistance-plate-x", &tmp);
	if (err) {
		dev_err(dev, "failed to get resistance-plate-x property\n");
		return err;
	}
	ts->x_plate_ohms = (u16)tmp;

	device_property_read_u8(dev, "adi,first-conversion-delay",
				&ts->first_conversion_delay);
	device_property_read_u8(dev, "adi,acquisition-time",
				&ts->acquisition_time);
	device_property_read_u8(dev, "adi,median-filter-size", &ts->median);
	device_property_read_u8(dev, "adi,averaging", &ts->averaging);
	device_property_read_u8(dev, "adi,conversion-interval",
				&ts->pen_down_acc_interval);

	ts->swap_xy = device_property_read_bool(dev, "touchscreen-swapped-x-y");

	return 0;
}

struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
			    const struct ad7879_bus_ops *bops)
			    const struct ad7879_bus_ops *bops)
{
{
@@ -495,43 +521,44 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
	u16 revid;
	u16 revid;


	if (!irq) {
	if (!irq) {
		dev_err(dev, "no IRQ?\n");
		dev_err(dev, "No IRQ specified\n");
		err = -EINVAL;
		return ERR_PTR(-EINVAL);
		goto err_out;
	}

	if (!pdata) {
		dev_err(dev, "no platform data?\n");
		err = -EINVAL;
		goto err_out;
	}
	}


	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
	ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
	input_dev = input_allocate_device();
	if (!ts)
	if (!ts || !input_dev) {
		return ERR_PTR(-ENOMEM);
		err = -ENOMEM;
		goto err_free_mem;
	}

	ts->bops = bops;
	ts->dev = dev;
	ts->input = input_dev;
	ts->irq = irq;


	/* Use swapped axis by default (backward compatibility) */
	if (pdata) {
		/* Platform data use swapped axis (backward compatibility) */
		ts->swap_xy = !pdata->swap_xy;
		ts->swap_xy = !pdata->swap_xy;


	setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts);

		ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
		ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
	ts->pressure_max = pdata->pressure_max ? : ~0;


		ts->first_conversion_delay = pdata->first_conversion_delay;
		ts->first_conversion_delay = pdata->first_conversion_delay;
		ts->acquisition_time = pdata->acquisition_time;
		ts->acquisition_time = pdata->acquisition_time;
		ts->averaging = pdata->averaging;
		ts->averaging = pdata->averaging;
		ts->pen_down_acc_interval = pdata->pen_down_acc_interval;
		ts->pen_down_acc_interval = pdata->pen_down_acc_interval;
		ts->median = pdata->median;
		ts->median = pdata->median;
	} else if (dev->of_node) {
		ad7879_parse_dt(dev, ts);
	} else {
		dev_err(dev, "No platform data\n");
		return ERR_PTR(-EINVAL);
	}


	input_dev = devm_input_allocate_device(dev);
	if (!input_dev) {
		dev_err(dev, "Failed to allocate input device\n");
		return ERR_PTR(-ENOMEM);
	}

	ts->bops = bops;
	ts->dev = dev;
	ts->input = input_dev;
	ts->irq = irq;

	setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts);
	snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));
	snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));


	input_dev->name = "AD7879 Touchscreen";
	input_dev->name = "AD7879 Touchscreen";
@@ -552,6 +579,7 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
	__set_bit(EV_KEY, input_dev->evbit);
	__set_bit(EV_KEY, input_dev->evbit);
	__set_bit(BTN_TOUCH, input_dev->keybit);
	__set_bit(BTN_TOUCH, input_dev->keybit);


	if (pdata) {
		input_set_abs_params(input_dev, ABS_X,
		input_set_abs_params(input_dev, ABS_X,
				pdata->x_min ? : 0,
				pdata->x_min ? : 0,
				pdata->x_max ? : MAX_12BIT,
				pdata->x_max ? : MAX_12BIT,
@@ -561,12 +589,23 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
				pdata->y_max ? : MAX_12BIT,
				pdata->y_max ? : MAX_12BIT,
				0, 0);
				0, 0);
		input_set_abs_params(input_dev, ABS_PRESSURE,
		input_set_abs_params(input_dev, ABS_PRESSURE,
			pdata->pressure_min, pdata->pressure_max, 0, 0);
				pdata->pressure_min,
				pdata->pressure_max ? : ~0,
				0, 0);
	} else {
		input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
		input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
		touchscreen_parse_properties(input_dev, false);
		if (!input_abs_get_max(input_dev, ABS_PRESSURE)) {
			dev_err(dev, "Touchscreen pressure is not specified\n");
			return ERR_PTR(-EINVAL);
		}
	}


	err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET);
	err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET);
	if (err < 0) {
	if (err < 0) {
		dev_err(dev, "Failed to write %s\n", input_dev->name);
		dev_err(dev, "Failed to write %s\n", input_dev->name);
		goto err_free_mem;
		return ERR_PTR(err);
	}
	}


	revid = ad7879_read(ts, AD7879_REG_REVID);
	revid = ad7879_read(ts, AD7879_REG_REVID);
@@ -575,8 +614,7 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
	if (input_dev->id.product != devid) {
	if (input_dev->id.product != devid) {
		dev_err(dev, "Failed to probe %s (%x vs %x)\n",
		dev_err(dev, "Failed to probe %s (%x vs %x)\n",
			input_dev->name, devid, revid);
			input_dev->name, devid, revid);
		err = -ENODEV;
		return ERR_PTR(-ENODEV);
		goto err_free_mem;
	}
	}


	ts->cmd_crtl3 = AD7879_YPLUS_BIT |
	ts->cmd_crtl3 = AD7879_YPLUS_BIT |
@@ -596,23 +634,25 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
			AD7879_ACQ(ts->acquisition_time) |
			AD7879_ACQ(ts->acquisition_time) |
			AD7879_TMR(ts->pen_down_acc_interval);
			AD7879_TMR(ts->pen_down_acc_interval);


	err = request_threaded_irq(ts->irq, NULL, ad7879_irq,
	err = devm_request_threaded_irq(dev, ts->irq, NULL, ad7879_irq,
					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
					dev_name(dev), ts);
					dev_name(dev), ts);
	if (err) {
	if (err) {
		dev_err(dev, "irq %d busy?\n", ts->irq);
		dev_err(dev, "Failed to request IRQ: %d\n", err);
		goto err_free_mem;
		return ERR_PTR(err);
	}
	}


	__ad7879_disable(ts);
	__ad7879_disable(ts);


	err = sysfs_create_group(&dev->kobj, &ad7879_attr_group);
	err = sysfs_create_group(&dev->kobj, &ad7879_attr_group);
	if (err)
	if (err)
		goto err_free_irq;
		goto err_out;


	if (pdata) {
		err = ad7879_gpio_add(ts, pdata);
		err = ad7879_gpio_add(ts, pdata);
		if (err)
		if (err)
			goto err_remove_attr;
			goto err_remove_attr;
	}


	err = input_register_device(input_dev);
	err = input_register_device(input_dev);
	if (err)
	if (err)
@@ -624,11 +664,6 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
	ad7879_gpio_remove(ts);
	ad7879_gpio_remove(ts);
err_remove_attr:
err_remove_attr:
	sysfs_remove_group(&dev->kobj, &ad7879_attr_group);
	sysfs_remove_group(&dev->kobj, &ad7879_attr_group);
err_free_irq:
	free_irq(ts->irq, ts);
err_free_mem:
	input_free_device(input_dev);
	kfree(ts);
err_out:
err_out:
	return ERR_PTR(err);
	return ERR_PTR(err);
}
}
@@ -638,9 +673,6 @@ void ad7879_remove(struct ad7879 *ts)
{
{
	ad7879_gpio_remove(ts);
	ad7879_gpio_remove(ts);
	sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group);
	sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group);
	free_irq(ts->irq, ts);
	input_unregister_device(ts->input);
	kfree(ts);
}
}
EXPORT_SYMBOL(ad7879_remove);
EXPORT_SYMBOL(ad7879_remove);