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

Commit 07f9e5cf authored by Denis Carikli's avatar Denis Carikli Committed by Dmitry Torokhov
Browse files

Input: tsc2007 - add device tree support.

parent bd77c321
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
* 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>;
		};

		/* ... */
	};
+1 −1
Original line number Diff line number Diff line
@@ -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);
}
+1 −1
Original line number Diff line number Diff line
@@ -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);
+1 −1
Original line number Diff line number Diff line
@@ -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);
+133 −40
Original line number Diff line number Diff line
@@ -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,
Loading