Title: [8908] trunk: [#6066] AD7160 Linux Driver Support

Diff

Modified: trunk/drivers/input/touchscreen/Kconfig (8907 => 8908)


--- trunk/drivers/input/touchscreen/Kconfig	2010-06-11 10:45:44 UTC (rev 8907)
+++ trunk/drivers/input/touchscreen/Kconfig	2010-06-11 14:34:04 UTC (rev 8908)
@@ -86,6 +86,19 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called ad7879-spi.
 
+config TOUCHSCREEN_AD7160
+	tristate "AD7160 based touchscreens"
+	depends on I2C
+	help
+	  Say Y here if you have a touchscreen interface using the
+	  AD7160 controller, and your board-specific initialization
+	  code includes that.
+
+	  If unsure, say N (but it's safe to say "Y").
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ad7160.
+
 config TOUCHSCREEN_BITSY
 	tristate "Compaq iPAQ H3600 (Bitsy) touchscreen"
 	depends on SA1100_BITSY

Modified: trunk/drivers/input/touchscreen/Makefile (8907 => 8908)


--- trunk/drivers/input/touchscreen/Makefile	2010-06-11 10:45:44 UTC (rev 8907)
+++ trunk/drivers/input/touchscreen/Makefile	2010-06-11 14:34:04 UTC (rev 8908)
@@ -48,3 +48,4 @@
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE)	+= mainstone-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+= zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o
+obj-$(CONFIG_TOUCHSCREEN_AD7160)	+= ad7160.o ad7160-i2c.o

Added: trunk/drivers/input/touchscreen/ad7160-i2c.c (0 => 8908)


--- trunk/drivers/input/touchscreen/ad7160-i2c.c	                        (rev 0)
+++ trunk/drivers/input/touchscreen/ad7160-i2c.c	2010-06-11 14:34:04 UTC (rev 8908)
@@ -0,0 +1,177 @@
+/*
+ * AD7160  touchscreen (I2C bus)
+ *
+ * Copyright (C) 2010 Michael Hennerich, Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/input.h>	/* BUS_I2C */
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include "ad7160.h"
+
+#define AD7160_DEVID		0x7160	/* AD7160 */
+
+#ifdef CONFIG_PM
+static int ad7160_i2c_suspend(struct i2c_client *client, pm_message_t message)
+{
+	ad7160_disable(&client->dev);
+	return 0;
+}
+
+static int ad7160_i2c_resume(struct i2c_client *client)
+{
+	ad7160_enable(&client->dev);
+	return 0;
+}
+#else
+# define ad7160_i2c_suspend NULL
+# define ad7160_i2c_resume  NULL
+#endif
+
+/* All registers are word-sized.
+ * AD7160 uses be32 high byte first convention.
+ */
+
+static int ad7160_raw_i2c_read(void *dev, u32 reg, u32 len, u32 *data)
+{
+	struct i2c_client *client = dev;
+	struct i2c_msg msg[2];
+	u32 block_data[MAX_DATA_CNT];
+	int ret, icnt;
+
+	block_data[0] = cpu_to_be32(reg);
+
+	msg[0].addr = client->addr;
+	msg[0].flags = client->flags & I2C_M_TEN;
+	msg[0].len = REG_SIZE_BYTES;
+	msg[0].buf = (char *)block_data;
+
+	msg[1].addr = client->addr;
+	msg[1].flags = client->flags & I2C_M_TEN;
+	msg[1].flags |= I2C_M_RD;
+	msg[1].len = len * REG_SIZE_BYTES;
+	msg[1].buf = (char *)block_data;
+
+	ret = i2c_transfer(client->adapter, msg, 2);
+	if (ret != 2) {
+		dev_err(&client->dev, "read error %d\n", ret);
+		return -EIO;
+	}
+
+	for (icnt = 0; icnt < len; icnt++)
+		data[icnt] = be32_to_cpu(block_data[icnt]);
+
+	return len;
+}
+
+static int ad7160_raw_i2c_write(void *dev, u32 reg, u32 len, u32 *data)
+{
+	struct i2c_client *client = dev;
+	int ret, icnt;
+	u32 block_data[MAX_DATA_CNT];
+
+	block_data[0] = cpu_to_be32(reg);
+
+	for (icnt = 0; icnt < len; icnt++)
+		block_data[icnt + 1] = cpu_to_be32(data[icnt]);
+
+	ret = i2c_master_send(client, (char *)block_data, (len + 1) * REG_SIZE_BYTES);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C write error\n");
+		return ret;
+	}
+	return len;
+
+}
+
+static int ad7160_i2c_read(void *dev, u32 reg)
+{
+	int ret;
+	u32 retval;
+	struct i2c_client *client = dev;
+
+	ret = ad7160_raw_i2c_read(client, reg, 1, &retval);
+
+	if (ret < 0) {
+		dev_err(&client->dev, "read error\n");
+		return ret;
+	}
+
+	return retval;
+}
+
+static int ad7160_i2c_write(void *dev, u32 reg, u32 val)
+{
+	struct i2c_client *client = dev;
+	int ret;
+	u32 retval = val;
+
+	ret = ad7160_raw_i2c_write(client, reg, 1, &retval);
+
+	if (ret < 0)
+		dev_err(&client->dev, "write error\n");
+
+	return ret;
+}
+
+static const struct ad7160_bus_ops bops = {
+	.read = ad7160_i2c_read,
+	.multi_read = ad7160_raw_i2c_read,
+	.write = ad7160_i2c_write,
+};
+
+static int __devinit ad7160_i2c_probe(struct i2c_client *client,
+				      const struct i2c_device_id *id)
+{
+	struct ad7160_bus_data bdata = {
+		.client = client,
+		.irq = client->irq,
+		.bops = &bops,
+	};
+
+	return ad7160_probe(&client->dev, &bdata, AD7160_DEVID, BUS_I2C);
+}
+
+static int __devexit ad7160_i2c_remove(struct i2c_client *client)
+{
+	return ad7160_remove(&client->dev);
+}
+
+static const struct i2c_device_id ad7160_id[] = {
+	{ "ad7160", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ad7160_id);
+
+static struct i2c_driver ad7160_i2c_driver = {
+	.driver = {
+		.name	= "ad7160",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ad7160_i2c_probe,
+	.remove		= __devexit_p(ad7160_i2c_remove),
+	.suspend	= ad7160_i2c_suspend,
+	.resume		= ad7160_i2c_resume,
+	.id_table	= ad7160_id,
+};
+
+static int __init ad7160_i2c_init(void)
+{
+	return i2c_add_driver(&ad7160_i2c_driver);
+}
+module_init(ad7160_i2c_init);
+
+static void __exit ad7160_i2c_exit(void)
+{
+	i2c_del_driver(&ad7160_i2c_driver);
+}
+module_exit(ad7160_i2c_exit);
+
+MODULE_AUTHOR("Michael Hennerich <[email protected]>");
+MODULE_DESCRIPTION("AD7160 touchscreen I2C bus driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("i2c:ad7160");

Added: trunk/drivers/input/touchscreen/ad7160.c (0 => 8908)


--- trunk/drivers/input/touchscreen/ad7160.c	                        (rev 0)
+++ trunk/drivers/input/touchscreen/ad7160.c	2010-06-11 14:34:04 UTC (rev 8908)
@@ -0,0 +1,587 @@
+/*
+ * AD7160 based touchscreen
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Copyright (C) 2010 Michael Hennerich, Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <asm/unaligned.h>
+
+#include <linux/input/ad7160.h>
+#include "ad7160.h"
+
+/* H/W registers */
+
+#define AD7160_REG_CHIPID		0x40002024
+#define AD7160_REG_AFE_PWR		0x40050000
+#define AD7160_REG_AFE_CFG1		0x40050004
+#define AD7160_REG_AFE_CFG2		0x40050008
+#define AD7160_REG_AFE_CFG3		0x4005000C
+#define AD7160_REG_AFE_AMB		0x40050018
+#define AD7160_REG_AFE_SLFAVG		0x4005001C
+#define AD7160_REG_AFE_MTLAVG		0x40050020
+#define AD7160_REG_AFE_DEVID		0x40050114
+#define AD7160_REG_AFE_TEST1		0x40050818
+#define AD7160_REG_AFE_TEST2		0x4005081C
+#define AD7160_REG_AFE_GND_OFFS0	0x40051040
+#define AD7160_REG_AFE_GND_CAL_OFFS0	0x400510C0
+
+/* S/W registers */
+
+#define AD7160_REG_DEVICE_ID		0x40051700
+#define AD7160_REG_REV_ID		0x40051704
+#define AD7160_REG_FW_REV		0x40051708
+#define AD7160_REG_XY_RES		0x40051710
+#define AD7160_REG_FINGER_ACT_CTRL	0x40051728
+#define AD7160_REG_FINGER_ACT_STAT	0x40051784
+#define AD7160_REG_GEST_STAT		0x40051788
+#define AD7160_REG_NB_FINGERS		0x4005178C
+#define AD7160_REG_POS_DATA_STATUS1	0x40051790
+#define AD7160_REG_INT_GEST_EN_CTRL	0x4005173C
+
+
+/* Debug registers */
+#define AD7160_REG_STAGE_READ_INDEX	0x40051800
+#define AD7160_REG_STAGE_READ_SLF_CDC	0x40051804
+
+#define AD7160_FORCE_CALIBRATION	0x800
+#define AD7160_IIR_FILTER_ENABLE	0x2000
+
+#define AD7160_DEMO_CTRL_STAT		0x40051740
+#define AD7160_LPM_CTRL			0x40051714
+#define AD7160_POSITION_WINDOW_CTRL	0x40051750
+
+#define AD7160_SIL_ID			0x7160
+
+/*
+ * Bit definition
+ */
+
+/* INT_GEST_EN_CTRL */
+#define AD7160_TAP_ENABLE			(1 << 5)
+#define AD7160_DBL_TAP_ENABLE			(1 << 6)
+#define AD7160_EOC_HINT_ENABLE			(1 << 30)
+#define AD7160_POS_HINT_ENABLE			(1 << 31)
+
+/* GEST_STAT */
+#define AD7160_TAP				(1 << 8)
+#define AD7160_DOUBLE_TAP			(1 << 9)
+
+/* LPM_CTRL */
+#define AD7160_PWR_SAVE_CONV			(1 << 26)
+#define AD7160_PROX_WK_UP			(1 << 28)
+#define AD7160_FINGER_ACT_WK_UP			(1 << 29)
+#define AD7160_LPM_LIFT_OFF_EN			(1 << 30)
+#define AD7160_SHUTDOWN				(1 << 31)
+
+/* DEMO_CTRL_STAT */
+#define AD7160_SLF_OFFS_ADJUST			(1 << 0)
+#define AD7160_MTL_OFFS_ADJUST			(1 << 1)
+#define AD7160_SW_IIR_FILTER_EN			(1 << 2)
+#define AD7160_EASY_SETUP			(1 << 3)
+#define AD7160_MTL_OFFS_ADJUST_BUSY		(1 << 30)
+#define AD7160_SLF_OFFS_ADJUST_BUSY		(1 << 31)
+
+#define AD7160_STAT_X_OFFS			0
+#define AD7160_STAT_X_MASK			0xFFF
+#define AD7160_STAT_Y_OFFS			12
+#define AD7160_STAT_Y_MASK			0xFFF
+#define AD7160_STAT_ID_OFFS			24
+#define AD7160_STAT_ID_MASK			0xF
+#define AD7160_STAT_ACT_OFFS			31
+#define AD7160_STAT_ACT_MASK			0x1
+#define AD7160_STAT_TMAJ_OFFS			0
+#define AD7160_STAT_TMAJ_MASK			0x1FFF
+#define AD7160_STAT_TMIN_OFFS			13
+#define AD7160_STAT_TMIN_MASK			0x1FFF
+
+#define AD7160_MAX_TRACKING_ID			0x0FFFF
+#define AD7160_TRACKING_ID_FREE			0xF0000
+
+struct ad7160 {
+	struct ad7160_bus_data	bdata;
+	struct input_dev	*input;
+	struct work_struct	work;
+	struct mutex		mutex;
+	struct ad7160_platform_data *pdata;
+	unsigned		disabled:1;	/* P: mutex */
+	unsigned		handle_gest:1;
+
+	u32			num_fingers;
+	u32			gest_stat;
+	u32			finger_data[MAX_NUM_FINGERS * 2];
+	u32			tracking_id;
+	u32			tracking_lut[MAX_NUM_FINGERS];
+	u32			event_cabs;
+	char			phys[32];
+};
+
+static int ad7160_read(struct ad7160 *ts, u32 reg)
+{
+	return ts->bdata.bops->read(ts->bdata.client, reg);
+}
+static int ad7160_multi_read(struct ad7160 *ts,
+	u32 first_reg, u32 count, u32 *buf)
+{
+	return ts->bdata.bops->multi_read(ts->bdata.client,
+		first_reg, count, buf);
+}
+static int ad7160_write(struct ad7160 *ts, u32 reg, u32 val)
+{
+	return ts->bdata.bops->write(ts->bdata.client, reg, val);
+}
+
+static unsigned ad7160_handle_tracking_id(struct ad7160 *ts,
+		unsigned id, unsigned act)
+{
+	unsigned ret;
+
+	if (ts->event_cabs & AD7160_TRACKING_ID_ASCENDING) {
+		id--; /* ID is reported as 1..10 */
+		if (act) {
+			if (ts->tracking_lut[id] == AD7160_TRACKING_ID_FREE) {
+				if (++ts->tracking_id > AD7160_MAX_TRACKING_ID)
+					ts->tracking_id = 1;
+				ts->tracking_lut[id] = ts->tracking_id;
+			}
+
+			return ts->tracking_lut[id];
+
+		} else {
+			if (ts->tracking_lut[id] == AD7160_TRACKING_ID_FREE)
+				dev_err(ts->input->dev.parent,
+					"Tracking ID table mismatch\n");
+
+			ret = ts->tracking_lut[id];
+			ts->tracking_lut[id] = AD7160_TRACKING_ID_FREE;
+			return ret;
+		}
+	}
+	return id;
+}
+
+static void ad7160_report_fingers(struct ad7160 *ts)
+{
+	struct input_dev *input_dev = ts->input;
+	u32 x, y, a, id, maj, min, i;
+
+	for (i = 0; i < (ts->num_fingers * 2); i += 2) {
+
+		x = (ts->finger_data[i] >> AD7160_STAT_X_OFFS) &
+			AD7160_STAT_X_MASK;
+		y = (ts->finger_data[i] >> AD7160_STAT_Y_OFFS) &
+			AD7160_STAT_Y_MASK;
+		a = (ts->finger_data[i] >> AD7160_STAT_ACT_OFFS) &
+			AD7160_STAT_ACT_MASK;
+		id = (ts->finger_data[i] >> AD7160_STAT_ID_OFFS) &
+			AD7160_STAT_ID_MASK;
+
+#ifdef AD7160_REPORTS_TOUCH_MAJOR
+		maj = (ts->finger_data[i + 1] >> AD7160_STAT_TMAJ_OFFS) &
+			AD7160_STAT_TMAJ_MASK;
+		min = (ts->finger_data[i + 1] >> AD7160_STAT_TMIN_OFFS) &
+			AD7160_STAT_TMIN_MASK;
+#else
+		maj = min = a*10;
+#endif
+
+		if ((ts->event_cabs & AD7160_TRADITIONAL_TS_EMULATION) && (i == 0)) {
+			input_report_abs(input_dev, ABS_X, x);
+			input_report_abs(input_dev, ABS_Y, y);
+			input_report_abs(input_dev, ABS_PRESSURE, a*10);
+			input_report_key(input_dev, BTN_TOUCH, a);
+			input_report_key(input_dev, BTN_TOOL_FINGER, 1);
+		}
+
+#ifdef ABS_MT_POSITION_X
+		if (ts->event_cabs & AD7160_EMIT_ABS_MT_TRACKING_ID) {
+			if (id < 1 || id > MAX_NUM_FINGERS)
+				dev_err(ts->input->dev.parent,
+					"invalid tracking ID (%d)\n", id);
+
+			input_report_abs(input_dev, ABS_MT_TRACKING_ID,
+				ad7160_handle_tracking_id(ts, id, a));
+		} else {
+			if (!a) {
+				input_mt_sync(input_dev);
+				continue;
+			}
+		}
+
+		input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+		input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+
+		if (ts->event_cabs & AD7160_EMIT_ABS_MT_TOUCH_MAJOR)
+			input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, maj);
+
+		if (ts->event_cabs & AD7160_EMIT_ABS_MT_TOUCH_MINOR)
+			input_report_abs(input_dev, ABS_MT_TOUCH_MINOR, min);
+
+		if (ts->event_cabs & AD7160_EMIT_ABS_MT_ORIENTATION)
+			input_report_abs(input_dev, ABS_MT_ORIENTATION, maj > min ? 1 : 0);
+
+		if (ts->event_cabs & AD7160_EMIT_ABS_MT_PRESSURE)
+			input_event(input_dev, EV_ABS, ABS_MT_PRESSURE, a);
+
+		input_mt_sync(input_dev);
+#endif
+	}
+}
+
+static void ad7160_report_key_single(struct input_dev *input, int key)
+{
+	input_report_key(input, key, true);
+	input_sync(input);
+	input_report_key(input, key, false);
+}
+
+static void ad7160_report_gestures(struct ad7160 *ts)
+{
+	if (ts->gest_stat & AD7160_TAP)
+		ad7160_report_key_single(ts->input,
+			ts->pdata->ev_code_tap);
+
+	if (ts->gest_stat & AD7160_DOUBLE_TAP)
+		ad7160_report_key_single(ts->input,
+			ts->pdata->ev_code_double_tap);
+}
+
+static void ad7160_report(struct ad7160 *ts)
+{
+	if (ts->num_fingers > 0)
+		ad7160_report_fingers(ts);
+
+	if (ts->gest_stat)
+		ad7160_report_gestures(ts);
+
+	input_sync(ts->input);
+}
+
+static void ad7160_work(struct work_struct *work)
+{
+	struct ad7160 *ts = container_of(work, struct ad7160, work);
+
+	if (ts->handle_gest) {
+		u32 dat[2];
+		ad7160_multi_read(ts, AD7160_REG_GEST_STAT, 2, dat);
+		ts->gest_stat = dat[0];		/* AD7160_REG_GEST_STAT */
+		ts->num_fingers = dat[1];	/* AD7160_REG_NB_FINGERS */
+	} else {
+		ts->num_fingers = ad7160_read(ts, AD7160_REG_NB_FINGERS);
+	}
+
+#ifndef ABS_MT_POSITION_X
+	/* For none MT enabled kernels we care only for the oldest contact */
+	if (ts->num_fingers > 0)
+		ts->num_fingers = 1;
+#endif
+
+	if ((ts->num_fingers > 0) && (ts->num_fingers <= MAX_NUM_FINGERS)) {
+		ad7160_multi_read(ts, AD7160_REG_POS_DATA_STATUS1,
+			ts->num_fingers * 2, ts->finger_data);
+	} else {
+		if (!ts->gest_stat)
+			dev_err(ts->input->dev.parent,
+				"invalid number of fingers (%d)\n",
+				ts->num_fingers);
+		ts->num_fingers = 0;
+	}
+
+	ad7160_report(ts);
+}
+
+static irqreturn_t ad7160_irq(int irq, void *handle)
+{
+	struct ad7160 *ts = handle;
+	if (!work_pending(&ts->work))
+		schedule_work(&ts->work);
+
+	return IRQ_HANDLED;
+}
+
+static void ad7160_setup(struct ad7160 *ts)
+{
+	int i;
+
+	for (i = 0; i < MAX_NUM_FINGERS; i++)
+		ts->tracking_lut[i] = AD7160_TRACKING_ID_FREE;
+
+	ts->tracking_id = 1;
+
+	ad7160_write(ts, AD7160_LPM_CTRL, 0);
+	ad7160_write(ts, AD7160_REG_INT_GEST_EN_CTRL,
+			(ts->pdata->ev_code_tap != 0 ? AD7160_TAP_ENABLE : 0) |
+			(ts->pdata->ev_code_double_tap != 0
+			? AD7160_DBL_TAP_ENABLE : 0) |
+			AD7160_POS_HINT_ENABLE);
+
+	/* Sensor resolution */
+	ad7160_write(ts, AD7160_REG_XY_RES,
+			(ts->pdata->coord_pref << 28) |
+			(ts->pdata->filter_coef << 24) |
+			(ts->pdata->sensor_y_res << 12) |
+			ts->pdata->sensor_x_res);
+
+	/* Reset demo control/status register */
+	ad7160_write(ts, AD7160_DEMO_CTRL_STAT, AD7160_SW_IIR_FILTER_EN);
+
+	/* Position window update control register */
+	ad7160_write(ts, AD7160_POSITION_WINDOW_CTRL,
+		(ts->pdata->move_window << 8) |
+		ts->pdata->first_touch_window);
+
+	ad7160_write(ts, AD7160_REG_FINGER_ACT_CTRL,
+		ts->pdata->finger_act_ctrl);
+}
+
+void ad7160_disable(struct device *dev)
+{
+	struct ad7160 *ts = dev_get_drvdata(dev);
+	mutex_lock(&ts->mutex);
+
+	if (!ts->disabled) {
+		ad7160_write(ts, AD7160_LPM_CTRL, AD7160_SHUTDOWN);
+		ts->disabled = 1;
+		disable_irq(ts->bdata.irq);
+		cancel_work_sync(&ts->work);
+	}
+
+	mutex_unlock(&ts->mutex);
+}
+EXPORT_SYMBOL(ad7160_disable);
+
+void ad7160_enable(struct device *dev)
+{
+	struct ad7160 *ts = dev_get_drvdata(dev);
+	mutex_lock(&ts->mutex);
+
+	if (ts->disabled) {
+		ad7160_setup(ts);
+		ts->disabled = 0;
+		enable_irq(ts->bdata.irq);
+	}
+
+	mutex_unlock(&ts->mutex);
+}
+EXPORT_SYMBOL(ad7160_enable);
+
+static ssize_t ad7160_disable_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct ad7160 *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", ts->disabled);
+}
+
+static ssize_t ad7160_disable_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	unsigned long val;
+	int error;
+
+	error = strict_strtoul(buf, 10, &val);
+	if (error)
+		return error;
+
+	if (val)
+		ad7160_disable(dev);
+	else
+		ad7160_enable(dev);
+
+	return count;
+}
+
+static DEVICE_ATTR(disable, 0664, ad7160_disable_show, ad7160_disable_store);
+
+static struct attribute *ad7160_attributes[] = {
+	&dev_attr_disable.attr,
+	NULL
+};
+
+static const struct attribute_group ad7160_attr_group = {
+	.attrs = ad7160_attributes,
+};
+
+__devinit int
+ad7160_probe(struct device *dev, struct ad7160_bus_data *bdata,
+		u32 devid, u16 bustype)
+{
+	struct input_dev *input_dev;
+	struct ad7160_platform_data *pdata = dev->platform_data;
+	int err;
+	u32 revid;
+	struct ad7160 *ts;
+
+	if (!bdata->irq) {
+		dev_err(dev, "no IRQ?\n");
+		return -ENODEV;
+	}
+
+	if (!pdata) {
+		dev_err(dev, "no platform data?\n");
+		return -ENODEV;
+	}
+
+	err = -ENOMEM;
+
+	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+	if (!ts)
+		goto err_free_ts_mem;
+
+	input_dev = input_allocate_device();
+	if (!input_dev)
+		goto err_free_ts_mem;
+
+	ts->bdata = *bdata;
+	ts->pdata = pdata;
+	ts->input = input_dev;
+	dev_set_drvdata(dev, ts);
+
+	ts->event_cabs = pdata->event_cabs;
+
+	INIT_WORK(&ts->work, ad7160_work);
+	mutex_init(&ts->mutex);
+
+	snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));
+
+	input_dev->name = "AD7160 Touchscreen";
+	input_dev->phys = ts->phys;
+	input_dev->dev.parent = dev;
+	input_dev->id.bustype = bustype;
+
+	__set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(EV_KEY, input_dev->evbit);
+
+	if (pdata->ev_code_tap) {
+		__set_bit(pdata->ev_code_tap, input_dev->keybit);
+		ts->handle_gest = true;
+	}
+	if (pdata->ev_code_double_tap) {
+		__set_bit(pdata->ev_code_double_tap, input_dev->keybit);
+		ts->handle_gest = true;
+	}
+
+	if (ts->event_cabs & AD7160_TRADITIONAL_TS_EMULATION) {
+
+		__set_bit(ABS_X, input_dev->absbit);
+		__set_bit(ABS_Y, input_dev->absbit);
+		__set_bit(ABS_PRESSURE, input_dev->absbit);
+
+		input_set_abs_params(input_dev, ABS_X, 0,
+				pdata->sensor_x_res, 0, 0);
+		input_set_abs_params(input_dev, ABS_Y, 0,
+				pdata->sensor_y_res, 0, 0);
+		input_set_abs_params(input_dev, ABS_PRESSURE, 0,
+				pdata->pressure, 0, 0);
+
+		__set_bit(BTN_TOUCH, input_dev->keybit);
+		__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+	}
+
+#ifdef ABS_MT_POSITION_X
+	if (ts->event_cabs & AD7160_EMIT_ABS_MT_TRACKING_ID)
+		input_set_abs_params(input_dev, ABS_MT_TRACKING_ID,
+			1, AD7160_MAX_TRACKING_ID, 0, 0);
+
+	if (ts->event_cabs & AD7160_EMIT_ABS_MT_PRESSURE)
+		input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 100, 0, 0);
+
+	if (ts->event_cabs & AD7160_EMIT_ABS_MT_TOUCH_MAJOR) {
+		input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0,
+			AD7160_STAT_TMAJ_MASK, 0, 0);
+	}
+
+	if (ts->event_cabs & AD7160_EMIT_ABS_MT_TOUCH_MINOR) {
+		input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0,
+			AD7160_STAT_TMIN_MASK, 0, 0);
+	}
+
+	if (ts->event_cabs & AD7160_EMIT_ABS_MT_ORIENTATION)
+		input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+
+	input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
+		ts->pdata->sensor_x_res, 0, 0);
+
+	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
+		ts->pdata->sensor_y_res, 0, 0);
+
+#endif
+	input_dev->id.product = ad7160_read(ts, AD7160_REG_DEVICE_ID) & 0xFFFF;
+
+	if (input_dev->id.product != AD7160_SIL_ID) {
+		dev_err(dev, "Failed to probe %s (%x vs %x)\n",
+			input_dev->name, input_dev->id.product, AD7160_SIL_ID);
+		err = -ENODEV;
+		goto err_free_mem;
+	}
+
+	input_dev->id.version = ad7160_read(ts, AD7160_REG_REV_ID) >> 16;
+	revid = ad7160_read(ts, AD7160_REG_FW_REV);
+
+	ad7160_setup(ts);
+
+	err = request_irq(ts->bdata.irq, ad7160_irq, IRQF_TRIGGER_FALLING,
+			  dev_name(dev), ts);
+	if (err) {
+		dev_err(dev, "irq %d busy?\n", ts->bdata.irq);
+		goto err_free_mem;
+	}
+
+	err = sysfs_create_group(&dev->kobj, &ad7160_attr_group);
+	if (err)
+		goto err_free_irq;
+
+	err = input_register_device(input_dev);
+	if (err)
+		goto err_remove_attr;
+
+	dev_info(dev, "Rev.%d touchscreen, irq %d Firmware REV %d.%d.%d.%d\n",
+		 input_dev->id.version, ts->bdata.irq, revid >> 12,
+		 (revid >> 8) & 0xF, (revid >> 4) & 0xF, revid & 0xF);
+
+	return 0;
+
+err_remove_attr:
+	sysfs_remove_group(&dev->kobj, &ad7160_attr_group);
+err_free_irq:
+	free_irq(ts->bdata.irq, ts);
+err_free_mem:
+	input_free_device(input_dev);
+err_free_ts_mem:
+	kfree(ts);
+	dev_set_drvdata(dev, NULL);
+
+	return err;
+}
+EXPORT_SYMBOL(ad7160_probe);
+
+__devexit int ad7160_remove(struct device *dev)
+{
+	struct ad7160 *ts = dev_get_drvdata(dev);
+
+	ad7160_disable(dev);
+	sysfs_remove_group(&dev->kobj, &ad7160_attr_group);
+	free_irq(ts->bdata.irq, ts);
+	input_unregister_device(ts->input);
+	dev_set_drvdata(dev, NULL);
+	kfree(ts);
+
+	dev_dbg(dev, "unregistered touchscreen\n");
+
+	return 0;
+}
+EXPORT_SYMBOL(ad7160_remove);
+
+MODULE_AUTHOR("Michael Hennerich <[email protected]>");
+MODULE_DESCRIPTION("AD7160 Touchscreen Driver");
+MODULE_LICENSE("GPL");

Added: trunk/drivers/input/touchscreen/ad7160.h (0 => 8908)


--- trunk/drivers/input/touchscreen/ad7160.h	                        (rev 0)
+++ trunk/drivers/input/touchscreen/ad7160.h	2010-06-11 14:34:04 UTC (rev 8908)
@@ -0,0 +1,40 @@
+/*
+ * AD7160 touchscreen (bus interfaces)
+ *
+ * Copyright (C) 2010 Michael Hennerich, Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _AD7160_H_
+#define _AD7160_H_
+
+#include <linux/types.h>
+
+#define REG_SIZE_BYTES		4
+#define MAX_NUM_FINGERS		10
+#define MAX_DATA_CNT		(MAX_NUM_FINGERS * 2)
+
+struct ad7160;
+
+struct ad7160_bus_ops {
+	int (*read) (void *dev, u32 reg);
+	int (*multi_read) (void *dev, u32 first_reg, u32 count, u32 *buf);
+	int (*write) (void *dev, u32 reg, u32 val);
+};
+struct ad7160_bus_data {
+	void *client;
+	int irq;
+	const struct ad7160_bus_ops *bops;
+};
+
+void ad7160_disable(struct device *dev);
+void ad7160_enable(struct device *dev);
+int ad7160_probe(struct device *dev, struct ad7160_bus_data *bdata, u32 devid, u16 bustype);
+int ad7160_remove(struct device *dev);
+
+#ifdef	CONFIG_AD7160_RAW_DATA_IFACE
+void ad7160_do_raw_data(struct ad7160_bus_data *bdata);
+#endif
+
+#endif

Added: trunk/include/linux/input/ad7160.h (0 => 8908)


--- trunk/include/linux/input/ad7160.h	                        (rev 0)
+++ trunk/include/linux/input/ad7160.h	2010-06-11 14:34:04 UTC (rev 8908)
@@ -0,0 +1,94 @@
+/*
+ * include/linux/input/ad7160.h
+ *
+ * Touchsceen characteristics are highly application specific
+ * and may vary between boards and models. The platform_data for the
+ * device's "struct device" holds this information.
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __LINUX_INPUT_AD7160_H__
+#define __LINUX_INPUT_AD7160_H__
+
+struct ad7160_platform_data {
+	u32 sensor_x_res;
+	u32 sensor_y_res;
+	u32 pressure;
+
+	/* Position filter Coefficient
+	 * This option is used to define the value of the
+	 * IIR filter used to smooth the response of the touch.
+	 * The value must range between 1 and 9.
+	 * The smaller the coefficient the bigger the averaging.
+	 */
+
+	u8 filter_coef;
+
+	/* This option is used to determine the
+	 * location of the (0,0) coordinates.
+	 */
+#define AD7160_ORIG_TOP_LEFT 0x8 /* (0,0) Coordinate is the TOP LEFT corner */
+#define AD7160_ORIG_TOP_RIGHT 0x4 /* (0,0) Coordinate is the TOP RIGHT corner */
+#define AD7160_ORIG_BOTTOM_LEFT 0x2 /* (0,0) Coordinate is the BOTTOM LEFT corner */
+#define AD7160_ORIG_BOTTOM_RIGHT 0x1 /* (0,0) Coordinate is the BOTTOM RIGHT corner */
+
+	u8 coord_pref;
+
+	/* This option sets a window around the first touch coordinate,
+	 * user needs to move outside this window to enable further press
+	 * events after first touch. Typically 4-6 positions to avoid
+	 * multiple press events when tapping or while touching
+	 * down on the sensor.
+	 */
+
+	u8 first_touch_window;
+
+	/* This option sets a window around the current touch coordinates
+	 * so the user needs to move by a certain number of coordinates
+	 * before a new press event is reported, typically -3 positions.
+	 * This gives the user the flexibility to reduce host interaction
+	 * and trade off linarity depending on the use case.
+	 */
+
+	u8 move_window;
+
+	/* This will determine the minimal amount of response per finger
+	 * in order to register a valid finger touch using mutual
+	 * capacitance measurements. (default 0x0064)
+	 */
+
+	u16 finger_act_ctrl;
+
+	/* Event Capabilities - MT devices may produce a lot of data.
+	 * In some applications only a subset is consumed by user space.
+	 * This options allows control for some optional events.
+	 */
+
+#define AD7160_TRADITIONAL_TS_EMULATION		(1 << 0)	/* EMIT: ABS_{X,Y,PRESSURE} & BTN_TOUCH */
+#define AD7160_EMIT_ABS_MT_TRACKING_ID		(1 << 1)
+#define AD7160_EMIT_ABS_MT_TOUCH_MAJOR		(1 << 2)
+#define AD7160_EMIT_ABS_MT_TOUCH_MINOR		(1 << 3)
+#define AD7160_EMIT_ABS_MT_ORIENTATION		(1 << 4)
+#define AD7160_EMIT_ABS_MT_PRESSURE		(1 << 5)
+#define AD7160_TRACKING_ID_ASCENDING		(1 << 6)
+
+	u32 event_cabs;
+
+	/*
+	 * A valid BTN or KEY Code; use 0 or KEY_RESERVED to disable
+	 * TAP event reporting.
+	 */
+
+	u32 ev_code_tap;
+
+	/*
+	 * A valid BTN or KEY Code; use 0 or KEY_RESERVED to disable
+	 * DOUBLE TAP event reporting.
+	 */
+
+	u32 ev_code_double_tap;
+};
+#endif
_______________________________________________
Linux-kernel-commits mailing list
[email protected]
https://blackfin.uclinux.org/mailman/listinfo/linux-kernel-commits

Reply via email to