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");