diff options
Diffstat (limited to 'drivers/input/touchscreen/ad7879.c')
| -rw-r--r-- | drivers/input/touchscreen/ad7879.c | 816 |
1 files changed, 344 insertions, 472 deletions
diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index 5d8a7039880..fce590677b7 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -1,24 +1,9 @@ /* - * Copyright (C) 2008 Michael Hennerich, Analog Devices Inc. + * AD7879/AD7889 based touchscreen and GPIO driver * - * Description: AD7879 based touchscreen, and GPIO driver (I2C/SPI Interface) + * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc. * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * Licensed under the GPL-2 or later. * * History: * Copyright (c) 2005 David Brownell @@ -37,17 +22,18 @@ */ #include <linux/device.h> -#include <linux/init.h> #include <linux/delay.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/slab.h> -#include <linux/workqueue.h> #include <linux/spi/spi.h> #include <linux/i2c.h> +#include <linux/gpio.h> #include <linux/spi/ad7879.h> +#include <linux/module.h> +#include "ad7879.h" #define AD7879_REG_ZEROS 0 #define AD7879_REG_CTRL1 1 @@ -118,28 +104,20 @@ enum { #define MAX_12BIT ((1<<12)-1) #define TS_PEN_UP_TIMEOUT msecs_to_jiffies(50) -#if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE) -#define AD7879_DEVID 0x7A -typedef struct spi_device bus_device; -#elif defined(CONFIG_TOUCHSCREEN_AD7879_I2C) || defined(CONFIG_TOUCHSCREEN_AD7879_I2C_MODULE) -#define AD7879_DEVID 0x79 -typedef struct i2c_client bus_device; -#endif - struct ad7879 { - bus_device *bus; + const struct ad7879_bus_ops *bops; + + struct device *dev; struct input_dev *input; - struct work_struct work; struct timer_list timer; - +#ifdef CONFIG_GPIOLIB + struct gpio_chip gc; struct mutex mutex; - unsigned disabled:1; /* P: mutex */ - -#if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE) - struct spi_message msg; - struct spi_transfer xfer[AD7879_NR_SENSE + 1]; - u16 cmd; #endif + unsigned int irq; + bool disabled; /* P: input->mutex */ + bool suspended; /* P: input->mutex */ + bool swap_xy; u16 conversion_data[AD7879_NR_SENSE]; char phys[32]; u8 first_conversion_delay; @@ -149,18 +127,30 @@ struct ad7879 { u8 median; u16 x_plate_ohms; u16 pressure_max; - u16 gpio_init; u16 cmd_crtl1; u16 cmd_crtl2; u16 cmd_crtl3; - unsigned gpio:1; + int x; + int y; + int Rt; }; -static int ad7879_read(bus_device *, u8); -static int ad7879_write(bus_device *, u8, u16); -static void ad7879_collect(struct ad7879 *); +static int ad7879_read(struct ad7879 *ts, u8 reg) +{ + return ts->bops->read(ts->dev, reg); +} -static void ad7879_report(struct ad7879 *ts) +static int ad7879_multi_read(struct ad7879 *ts, u8 first_reg, u8 count, u16 *buf) +{ + return ts->bops->multi_read(ts->dev, first_reg, count, buf); +} + +static int ad7879_write(struct ad7879 *ts, u8 reg, u16 val) +{ + return ts->bops->write(ts->dev, reg, val); +} + +static int ad7879_report(struct ad7879 *ts) { struct input_dev *input_dev = ts->input; unsigned Rt; @@ -171,14 +161,19 @@ static void ad7879_report(struct ad7879 *ts) z1 = ts->conversion_data[AD7879_SEQ_Z1] & MAX_12BIT; z2 = ts->conversion_data[AD7879_SEQ_Z2] & MAX_12BIT; + if (ts->swap_xy) + swap(x, y); + /* * The samples processed here are already preprocessed by the AD7879. - * The preprocessing function consists of a median and an averaging filter. - * The combination of these two techniques provides a robust solution, - * discarding the spurious noise in the signal and keeping only the data of interest. - * The size of both filters is programmable. (dev.platform_data, see linux/spi/ad7879.h) - * Other user-programmable conversion controls include variable acquisition time, - * and first conversion delay. Up to 16 averages can be taken per conversion. + * The preprocessing function consists of a median and an averaging + * filter. The combination of these two techniques provides a robust + * solution, discarding the spurious noise in the signal and keeping + * only the data of interest. The size of both filters is + * programmable. (dev.platform_data, see linux/spi/ad7879.h) Other + * user-programmable conversion controls include variable acquisition + * time, and first conversion delay. Up to 16 averages can be taken + * per conversion. */ if (likely(x && z1)) { @@ -187,21 +182,36 @@ static void ad7879_report(struct ad7879 *ts) Rt /= z1; Rt = (Rt + 2047) >> 12; - input_report_abs(input_dev, ABS_X, x); - input_report_abs(input_dev, ABS_Y, y); - input_report_abs(input_dev, ABS_PRESSURE, Rt); - input_sync(input_dev); + /* + * Sample found inconsistent, pressure is beyond + * the maximum. Don't report it to user space. + */ + if (Rt > ts->pressure_max) + return -EINVAL; + + /* + * Note that we delay reporting events by one sample. + * This is done to avoid reporting last sample of the + * touch sequence, which may be incomplete if finger + * leaves the surface before last reading is taken. + */ + if (timer_pending(&ts->timer)) { + /* Touch continues */ + input_report_key(input_dev, BTN_TOUCH, 1); + input_report_abs(input_dev, ABS_X, ts->x); + input_report_abs(input_dev, ABS_Y, ts->y); + input_report_abs(input_dev, ABS_PRESSURE, ts->Rt); + input_sync(input_dev); + } + + ts->x = x; + ts->y = y; + ts->Rt = Rt; + + return 0; } -} -static void ad7879_work(struct work_struct *work) -{ - struct ad7879 *ts = container_of(work, struct ad7879, work); - - /* use keventd context to read the result registers */ - ad7879_collect(ts); - ad7879_report(ts); - mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); + return -EINVAL; } static void ad7879_ts_event_release(struct ad7879 *ts) @@ -209,6 +219,7 @@ static void ad7879_ts_event_release(struct ad7879 *ts) struct input_dev *input_dev = ts->input; input_report_abs(input_dev, ABS_PRESSURE, 0); + input_report_key(input_dev, BTN_TOUCH, 0); input_sync(input_dev); } @@ -223,74 +234,111 @@ static irqreturn_t ad7879_irq(int irq, void *handle) { struct ad7879 *ts = handle; - /* The repeated conversion sequencer controlled by TMR kicked off too fast. - * We ignore the last and process the sample sequence currently in the queue. - * It can't be older than 9.4ms - */ + ad7879_multi_read(ts, AD7879_REG_XPLUS, AD7879_NR_SENSE, ts->conversion_data); - if (!work_pending(&ts->work)) - schedule_work(&ts->work); + if (!ad7879_report(ts)) + mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); return IRQ_HANDLED; } -static void ad7879_setup(struct ad7879 *ts) +static void __ad7879_enable(struct ad7879 *ts) { - ts->cmd_crtl3 = AD7879_YPLUS_BIT | - AD7879_XPLUS_BIT | - AD7879_Z2_BIT | - AD7879_Z1_BIT | - AD7879_TEMPMASK_BIT | - AD7879_AUXVBATMASK_BIT | - AD7879_GPIOALERTMASK_BIT; + ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); + ad7879_write(ts, AD7879_REG_CTRL3, ts->cmd_crtl3); + ad7879_write(ts, AD7879_REG_CTRL1, ts->cmd_crtl1); - ts->cmd_crtl2 = AD7879_PM(AD7879_PM_DYN) | AD7879_DFR | - AD7879_AVG(ts->averaging) | - AD7879_MFS(ts->median) | - AD7879_FCD(ts->first_conversion_delay) | - ts->gpio_init; + enable_irq(ts->irq); +} - ts->cmd_crtl1 = AD7879_MODE_INT | AD7879_MODE_SEQ1 | - AD7879_ACQ(ts->acquisition_time) | - AD7879_TMR(ts->pen_down_acc_interval); +static void __ad7879_disable(struct ad7879 *ts) +{ + u16 reg = (ts->cmd_crtl2 & ~AD7879_PM(-1)) | + AD7879_PM(AD7879_PM_SHUTDOWN); + disable_irq(ts->irq); + + if (del_timer_sync(&ts->timer)) + ad7879_ts_event_release(ts); - ad7879_write(ts->bus, AD7879_REG_CTRL2, ts->cmd_crtl2); - ad7879_write(ts->bus, AD7879_REG_CTRL3, ts->cmd_crtl3); - ad7879_write(ts->bus, AD7879_REG_CTRL1, ts->cmd_crtl1); + ad7879_write(ts, AD7879_REG_CTRL2, reg); } -static void ad7879_disable(struct ad7879 *ts) + +static int ad7879_open(struct input_dev *input) { - mutex_lock(&ts->mutex); + struct ad7879 *ts = input_get_drvdata(input); - if (!ts->disabled) { + /* protected by input->mutex */ + if (!ts->disabled && !ts->suspended) + __ad7879_enable(ts); - ts->disabled = 1; - disable_irq(ts->bus->irq); + return 0; +} - cancel_work_sync(&ts->work); +static void ad7879_close(struct input_dev* input) +{ + struct ad7879 *ts = input_get_drvdata(input); - if (del_timer_sync(&ts->timer)) - ad7879_ts_event_release(ts); + /* protected by input->mutex */ + if (!ts->disabled && !ts->suspended) + __ad7879_disable(ts); +} - ad7879_write(ts->bus, AD7879_REG_CTRL2, - AD7879_PM(AD7879_PM_SHUTDOWN)); - } +#ifdef CONFIG_PM_SLEEP +static int ad7879_suspend(struct device *dev) +{ + struct ad7879 *ts = dev_get_drvdata(dev); - mutex_unlock(&ts->mutex); + mutex_lock(&ts->input->mutex); + + if (!ts->suspended && !ts->disabled && ts->input->users) + __ad7879_disable(ts); + + ts->suspended = true; + + mutex_unlock(&ts->input->mutex); + + return 0; } -static void ad7879_enable(struct ad7879 *ts) +static int ad7879_resume(struct device *dev) { - mutex_lock(&ts->mutex); + struct ad7879 *ts = dev_get_drvdata(dev); + + mutex_lock(&ts->input->mutex); + + if (ts->suspended && !ts->disabled && ts->input->users) + __ad7879_enable(ts); - if (ts->disabled) { - ad7879_setup(ts); - ts->disabled = 0; - enable_irq(ts->bus->irq); + ts->suspended = false; + + mutex_unlock(&ts->input->mutex); + + return 0; +} +#endif + +SIMPLE_DEV_PM_OPS(ad7879_pm_ops, ad7879_suspend, ad7879_resume); +EXPORT_SYMBOL(ad7879_pm_ops); + +static void ad7879_toggle(struct ad7879 *ts, bool disable) +{ + mutex_lock(&ts->input->mutex); + + if (!ts->suspended && ts->input->users != 0) { + + if (disable) { + if (ts->disabled) + __ad7879_enable(ts); + } else { + if (!ts->disabled) + __ad7879_disable(ts); + } } - mutex_unlock(&ts->mutex); + ts->disabled = disable; + + mutex_unlock(&ts->input->mutex); } static ssize_t ad7879_disable_show(struct device *dev, @@ -306,92 +354,178 @@ static ssize_t ad7879_disable_store(struct device *dev, const char *buf, size_t count) { struct ad7879 *ts = dev_get_drvdata(dev); - unsigned long val; + unsigned int val; int error; - error = strict_strtoul(buf, 10, &val); + error = kstrtouint(buf, 10, &val); if (error) return error; - if (val) - ad7879_disable(ts); - else - ad7879_enable(ts); + ad7879_toggle(ts, val); return count; } static DEVICE_ATTR(disable, 0664, ad7879_disable_show, ad7879_disable_store); -static ssize_t ad7879_gpio_show(struct device *dev, - struct device_attribute *attr, char *buf) +static struct attribute *ad7879_attributes[] = { + &dev_attr_disable.attr, + NULL +}; + +static const struct attribute_group ad7879_attr_group = { + .attrs = ad7879_attributes, +}; + +#ifdef CONFIG_GPIOLIB +static int ad7879_gpio_direction_input(struct gpio_chip *chip, + unsigned gpio) { - struct ad7879 *ts = dev_get_drvdata(dev); + struct ad7879 *ts = container_of(chip, struct ad7879, gc); + int err; + + mutex_lock(&ts->mutex); + ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIODIR | AD7879_GPIOPOL; + err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); + mutex_unlock(&ts->mutex); - return sprintf(buf, "%u\n", ts->gpio); + return err; } -static ssize_t ad7879_gpio_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int ad7879_gpio_direction_output(struct gpio_chip *chip, + unsigned gpio, int level) { - struct ad7879 *ts = dev_get_drvdata(dev); - unsigned long val; - int error; + struct ad7879 *ts = container_of(chip, struct ad7879, gc); + int err; - error = strict_strtoul(buf, 10, &val); - if (error) - return error; + mutex_lock(&ts->mutex); + ts->cmd_crtl2 &= ~AD7879_GPIODIR; + ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIOPOL; + if (level) + ts->cmd_crtl2 |= AD7879_GPIO_DATA; + else + ts->cmd_crtl2 &= ~AD7879_GPIO_DATA; + + err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); + mutex_unlock(&ts->mutex); + + return err; +} + +static int ad7879_gpio_get_value(struct gpio_chip *chip, unsigned gpio) +{ + struct ad7879 *ts = container_of(chip, struct ad7879, gc); + u16 val; mutex_lock(&ts->mutex); - ts->gpio = !!val; - error = ad7879_write(ts->bus, AD7879_REG_CTRL2, - ts->gpio ? - ts->cmd_crtl2 & ~AD7879_GPIO_DATA : - ts->cmd_crtl2 | AD7879_GPIO_DATA); + val = ad7879_read(ts, AD7879_REG_CTRL2); mutex_unlock(&ts->mutex); - return error ? : count; + return !!(val & AD7879_GPIO_DATA); } -static DEVICE_ATTR(gpio, 0664, ad7879_gpio_show, ad7879_gpio_store); +static void ad7879_gpio_set_value(struct gpio_chip *chip, + unsigned gpio, int value) +{ + struct ad7879 *ts = container_of(chip, struct ad7879, gc); -static struct attribute *ad7879_attributes[] = { - &dev_attr_disable.attr, - &dev_attr_gpio.attr, - NULL -}; + mutex_lock(&ts->mutex); + if (value) + ts->cmd_crtl2 |= AD7879_GPIO_DATA; + else + ts->cmd_crtl2 &= ~AD7879_GPIO_DATA; -static const struct attribute_group ad7879_attr_group = { - .attrs = ad7879_attributes, -}; + ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); + mutex_unlock(&ts->mutex); +} + +static int ad7879_gpio_add(struct ad7879 *ts, + const struct ad7879_platform_data *pdata) +{ + int ret = 0; + + mutex_init(&ts->mutex); + + if (pdata->gpio_export) { + ts->gc.direction_input = ad7879_gpio_direction_input; + ts->gc.direction_output = ad7879_gpio_direction_output; + ts->gc.get = ad7879_gpio_get_value; + ts->gc.set = ad7879_gpio_set_value; + ts->gc.can_sleep = 1; + ts->gc.base = pdata->gpio_base; + ts->gc.ngpio = 1; + ts->gc.label = "AD7879-GPIO"; + ts->gc.owner = THIS_MODULE; + ts->gc.dev = ts->dev; + + ret = gpiochip_add(&ts->gc); + if (ret) + dev_err(ts->dev, "failed to register gpio %d\n", + ts->gc.base); + } + + return ret; +} + +static void ad7879_gpio_remove(struct ad7879 *ts) +{ + const struct ad7879_platform_data *pdata = dev_get_platdata(ts->dev); + int ret; + + if (pdata->gpio_export) { + ret = gpiochip_remove(&ts->gc); + if (ret) + dev_err(ts->dev, "failed to remove gpio %d\n", + ts->gc.base); + } +} +#else +static inline int ad7879_gpio_add(struct ad7879 *ts, + const struct ad7879_platform_data *pdata) +{ + return 0; +} + +static inline void ad7879_gpio_remove(struct ad7879 *ts) +{ +} +#endif -static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts) +struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, + const struct ad7879_bus_ops *bops) { + struct ad7879_platform_data *pdata = dev_get_platdata(dev); + struct ad7879 *ts; struct input_dev *input_dev; - struct ad7879_platform_data *pdata = bus->dev.platform_data; int err; u16 revid; - if (!bus->irq) { - dev_err(&bus->dev, "no IRQ?\n"); - return -ENODEV; + if (!irq) { + dev_err(dev, "no IRQ?\n"); + err = -EINVAL; + goto err_out; } if (!pdata) { - dev_err(&bus->dev, "no platform data?\n"); - return -ENODEV; + dev_err(dev, "no platform data?\n"); + err = -EINVAL; + goto err_out; } + ts = kzalloc(sizeof(*ts), GFP_KERNEL); input_dev = input_allocate_device(); - if (!input_dev) - return -ENOMEM; + if (!ts || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + ts->bops = bops; + ts->dev = dev; ts->input = input_dev; + ts->irq = irq; + ts->swap_xy = pdata->swap_xy; setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts); - INIT_WORK(&ts->work, ad7879_work); - mutex_init(&ts->mutex); ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; ts->pressure_max = pdata->pressure_max ? : ~0; @@ -402,23 +536,26 @@ static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts) ts->pen_down_acc_interval = pdata->pen_down_acc_interval; ts->median = pdata->median; - if (pdata->gpio_output) - ts->gpio_init = AD7879_GPIO_EN | - (pdata->gpio_default ? 0 : AD7879_GPIO_DATA); - else - ts->gpio_init = AD7879_GPIO_EN | AD7879_GPIODIR; - - snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&bus->dev)); + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); input_dev->name = "AD7879 Touchscreen"; input_dev->phys = ts->phys; - input_dev->dev.parent = &bus->dev; + input_dev->dev.parent = dev; + input_dev->id.bustype = bops->bustype; + + input_dev->open = ad7879_open; + input_dev->close = ad7879_close; + + input_set_drvdata(input_dev, ts); __set_bit(EV_ABS, input_dev->evbit); __set_bit(ABS_X, input_dev->absbit); __set_bit(ABS_Y, input_dev->absbit); __set_bit(ABS_PRESSURE, input_dev->absbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + input_set_abs_params(input_dev, ABS_X, pdata->x_min ? : 0, pdata->x_max ? : MAX_12BIT, @@ -430,351 +567,86 @@ static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts) input_set_abs_params(input_dev, ABS_PRESSURE, pdata->pressure_min, pdata->pressure_max, 0, 0); - err = ad7879_write(bus, AD7879_REG_CTRL2, AD7879_RESET); - + err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET); if (err < 0) { - dev_err(&bus->dev, "Failed to write %s\n", input_dev->name); + dev_err(dev, "Failed to write %s\n", input_dev->name); goto err_free_mem; } - revid = ad7879_read(bus, AD7879_REG_REVID); - - if ((revid & 0xFF) != AD7879_DEVID) { - dev_err(&bus->dev, "Failed to probe %s\n", input_dev->name); + revid = ad7879_read(ts, AD7879_REG_REVID); + input_dev->id.product = (revid & 0xff); + input_dev->id.version = revid >> 8; + if (input_dev->id.product != devid) { + dev_err(dev, "Failed to probe %s (%x vs %x)\n", + input_dev->name, devid, revid); err = -ENODEV; goto err_free_mem; } - ad7879_setup(ts); + ts->cmd_crtl3 = AD7879_YPLUS_BIT | + AD7879_XPLUS_BIT | + AD7879_Z2_BIT | + AD7879_Z1_BIT | + AD7879_TEMPMASK_BIT | + AD7879_AUXVBATMASK_BIT | + AD7879_GPIOALERTMASK_BIT; - err = request_irq(bus->irq, ad7879_irq, - IRQF_TRIGGER_FALLING, bus->dev.driver->name, ts); + ts->cmd_crtl2 = AD7879_PM(AD7879_PM_DYN) | AD7879_DFR | + AD7879_AVG(ts->averaging) | + AD7879_MFS(ts->median) | + AD7879_FCD(ts->first_conversion_delay); + ts->cmd_crtl1 = AD7879_MODE_INT | AD7879_MODE_SEQ1 | + AD7879_ACQ(ts->acquisition_time) | + AD7879_TMR(ts->pen_down_acc_interval); + + err = request_threaded_irq(ts->irq, NULL, ad7879_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(dev), ts); if (err) { - dev_err(&bus->dev, "irq %d busy?\n", bus->irq); + dev_err(dev, "irq %d busy?\n", ts->irq); goto err_free_mem; } - err = sysfs_create_group(&bus->dev.kobj, &ad7879_attr_group); + __ad7879_disable(ts); + + err = sysfs_create_group(&dev->kobj, &ad7879_attr_group); if (err) goto err_free_irq; - err = input_register_device(input_dev); + err = ad7879_gpio_add(ts, pdata); if (err) goto err_remove_attr; - dev_info(&bus->dev, "Rev.%d touchscreen, irq %d\n", - revid >> 8, bus->irq); + err = input_register_device(input_dev); + if (err) + goto err_remove_gpio; - return 0; + return ts; +err_remove_gpio: + ad7879_gpio_remove(ts); err_remove_attr: - sysfs_remove_group(&bus->dev.kobj, &ad7879_attr_group); + sysfs_remove_group(&dev->kobj, &ad7879_attr_group); err_free_irq: - free_irq(bus->irq, ts); + free_irq(ts->irq, ts); err_free_mem: input_free_device(input_dev); - - return err; -} - -static int __devexit ad7879_destroy(bus_device *bus, struct ad7879 *ts) -{ - ad7879_disable(ts); - sysfs_remove_group(&ts->bus->dev.kobj, &ad7879_attr_group); - free_irq(ts->bus->irq, ts); - input_unregister_device(ts->input); - dev_dbg(&bus->dev, "unregistered touchscreen\n"); - - return 0; -} - -#ifdef CONFIG_PM -static int ad7879_suspend(bus_device *bus, pm_message_t message) -{ - struct ad7879 *ts = dev_get_drvdata(&bus->dev); - - ad7879_disable(ts); - - return 0; -} - -static int ad7879_resume(bus_device *bus) -{ - struct ad7879 *ts = dev_get_drvdata(&bus->dev); - - ad7879_enable(ts); - - return 0; -} -#else -#define ad7879_suspend NULL -#define ad7879_resume NULL -#endif - -#if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE) -#define MAX_SPI_FREQ_HZ 5000000 -#define AD7879_CMD_MAGIC 0xE000 -#define AD7879_CMD_READ (1 << 10) -#define AD7879_WRITECMD(reg) (AD7879_CMD_MAGIC | (reg & 0xF)) -#define AD7879_READCMD(reg) (AD7879_CMD_MAGIC | AD7879_CMD_READ | (reg & 0xF)) - -struct ser_req { - u16 command; - u16 data; - struct spi_message msg; - struct spi_transfer xfer[2]; -}; - -/* - * ad7879_read/write are only used for initial setup and for sysfs controls. - * The main traffic is done in ad7879_collect(). - */ - -static int ad7879_read(struct spi_device *spi, u8 reg) -{ - struct ser_req *req; - int status, ret; - - req = kzalloc(sizeof *req, GFP_KERNEL); - if (!req) - return -ENOMEM; - - spi_message_init(&req->msg); - - req->command = (u16) AD7879_READCMD(reg); - req->xfer[0].tx_buf = &req->command; - req->xfer[0].len = 2; - - req->xfer[1].rx_buf = &req->data; - req->xfer[1].len = 2; - - spi_message_add_tail(&req->xfer[0], &req->msg); - spi_message_add_tail(&req->xfer[1], &req->msg); - - status = spi_sync(spi, &req->msg); - ret = status ? : req->data; - - kfree(req); - - return ret; -} - -static int ad7879_write(struct spi_device *spi, u8 reg, u16 val) -{ - struct ser_req *req; - int status; - - req = kzalloc(sizeof *req, GFP_KERNEL); - if (!req) - return -ENOMEM; - - spi_message_init(&req->msg); - - req->command = (u16) AD7879_WRITECMD(reg); - req->xfer[0].tx_buf = &req->command; - req->xfer[0].len = 2; - - req->data = val; - req->xfer[1].tx_buf = &req->data; - req->xfer[1].len = 2; - - spi_message_add_tail(&req->xfer[0], &req->msg); - spi_message_add_tail(&req->xfer[1], &req->msg); - - status = spi_sync(spi, &req->msg); - - kfree(req); - - return status; -} - -static void ad7879_collect(struct ad7879 *ts) -{ - int status = spi_sync(ts->bus, &ts->msg); - - if (status) - dev_err(&ts->bus->dev, "spi_sync --> %d\n", status); -} - -static void ad7879_setup_ts_def_msg(struct ad7879 *ts) -{ - struct spi_message *m; - int i; - - ts->cmd = (u16) AD7879_READCMD(AD7879_REG_XPLUS); - - m = &ts->msg; - spi_message_init(m); - ts->xfer[0].tx_buf = &ts->cmd; - ts->xfer[0].len = 2; - - spi_message_add_tail(&ts->xfer[0], m); - - for (i = 0; i < AD7879_NR_SENSE; i++) { - ts->xfer[i + 1].rx_buf = &ts->conversion_data[i]; - ts->xfer[i + 1].len = 2; - spi_message_add_tail(&ts->xfer[i + 1], m); - } -} - -static int __devinit ad7879_probe(struct spi_device *spi) -{ - struct ad7879 *ts; - int error; - - /* don't exceed max specified SPI CLK frequency */ - if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) { - dev_err(&spi->dev, "SPI CLK %d Hz?\n", spi->max_speed_hz); - return -EINVAL; - } - - ts = kzalloc(sizeof(struct ad7879), GFP_KERNEL); - if (!ts) - return -ENOMEM; - - dev_set_drvdata(&spi->dev, ts); - ts->bus = spi; - - ad7879_setup_ts_def_msg(ts); - - error = ad7879_construct(spi, ts); - if (error) { - dev_set_drvdata(&spi->dev, NULL); - kfree(ts); - } - - return 0; -} - -static int __devexit ad7879_remove(struct spi_device *spi) -{ - struct ad7879 *ts = dev_get_drvdata(&spi->dev); - - ad7879_destroy(spi, ts); - dev_set_drvdata(&spi->dev, NULL); kfree(ts); - - return 0; -} - -static struct spi_driver ad7879_driver = { - .driver = { - .name = "ad7879", - .bus = &spi_bus_type, - .owner = THIS_MODULE, - }, - .probe = ad7879_probe, - .remove = __devexit_p(ad7879_remove), - .suspend = ad7879_suspend, - .resume = ad7879_resume, -}; - -static int __init ad7879_init(void) -{ - return spi_register_driver(&ad7879_driver); +err_out: + return ERR_PTR(err); } -module_init(ad7879_init); +EXPORT_SYMBOL(ad7879_probe); -static void __exit ad7879_exit(void) +void ad7879_remove(struct ad7879 *ts) { - spi_unregister_driver(&ad7879_driver); -} -module_exit(ad7879_exit); - -#elif defined(CONFIG_TOUCHSCREEN_AD7879_I2C) || defined(CONFIG_TOUCHSCREEN_AD7879_I2C_MODULE) - -/* All registers are word-sized. - * AD7879 uses a high-byte first convention. - */ -static int ad7879_read(struct i2c_client *client, u8 reg) -{ - return swab16(i2c_smbus_read_word_data(client, reg)); -} - -static int ad7879_write(struct i2c_client *client, u8 reg, u16 val) -{ - return i2c_smbus_write_word_data(client, reg, swab16(val)); -} - -static void ad7879_collect(struct ad7879 *ts) -{ - int i; - - for (i = 0; i < AD7879_NR_SENSE; i++) - ts->conversion_data[i] = ad7879_read(ts->bus, - AD7879_REG_XPLUS + i); -} - -static int __devinit ad7879_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct ad7879 *ts; - int error; - - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_WORD_DATA)) { - dev_err(&client->dev, "SMBUS Word Data not Supported\n"); - return -EIO; - } - - ts = kzalloc(sizeof(struct ad7879), GFP_KERNEL); - if (!ts) - return -ENOMEM; - - i2c_set_clientdata(client, ts); - ts->bus = client; - - error = ad7879_construct(client, ts); - if (error) { - i2c_set_clientdata(client, NULL); - kfree(ts); - } - - return 0; -} - -static int __devexit ad7879_remove(struct i2c_client *client) -{ - struct ad7879 *ts = dev_get_drvdata(&client->dev); - - ad7879_destroy(client, ts); - i2c_set_clientdata(client, NULL); + ad7879_gpio_remove(ts); + sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group); + free_irq(ts->irq, ts); + input_unregister_device(ts->input); kfree(ts); - - return 0; -} - -static const struct i2c_device_id ad7879_id[] = { - { "ad7879", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ad7879_id); - -static struct i2c_driver ad7879_driver = { - .driver = { - .name = "ad7879", - .owner = THIS_MODULE, - }, - .probe = ad7879_probe, - .remove = __devexit_p(ad7879_remove), - .suspend = ad7879_suspend, - .resume = ad7879_resume, - .id_table = ad7879_id, -}; - -static int __init ad7879_init(void) -{ - return i2c_add_driver(&ad7879_driver); } -module_init(ad7879_init); - -static void __exit ad7879_exit(void) -{ - i2c_del_driver(&ad7879_driver); -} -module_exit(ad7879_exit); -#endif +EXPORT_SYMBOL(ad7879_remove); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver"); |
