diff options
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/keyboard/Kconfig | 7 | ||||
-rw-r--r-- | drivers/input/keyboard/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/keyboard/tegra-kbc.c | 700 | ||||
-rw-r--r-- | drivers/input/misc/Kconfig | 16 | ||||
-rw-r--r-- | drivers/input/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/misc/alps_gpio_scrollwheel.c | 428 | ||||
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 7 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 1 | ||||
-rwxr-xr-x | drivers/input/touchscreen/atmel_maxtouch.c | 2056 |
9 files changed, 3217 insertions, 0 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 9cc488d21490..149a32fa8372 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -327,6 +327,13 @@ config KEYBOARD_NEWTON To compile this driver as a module, choose M here: the module will be called newtonkbd. +config KEYBOARD_TEGRA + boolean "NVIDIA Tegra internal matrix keyboard controller support" + depends on ARCH_TEGRA + help + Say Y here if you want to use a matrix keyboard connected directly + to the internal keyboard controller on Tegra SoCs + config KEYBOARD_OPENCORES tristate "OpenCores Keyboard Controller" help diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 504b591be0cd..2c7686eb90b4 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o +obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c new file mode 100644 index 000000000000..b524436b88f5 --- /dev/null +++ b/drivers/input/keyboard/tegra-kbc.c @@ -0,0 +1,700 @@ +/* + * drivers/input/keyboard/tegra-kbc.c + * + * Keyboard class input driver for the NVIDIA Tegra SoC internal matrix + * keyboard controller + * + * Copyright (c) 2009-2010, NVIDIA Corporation. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/*#define DEBUG 1*/ +/*#define VERBOSE_DEBUG 1*/ +#include <linux/module.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/kthread.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <mach/clk.h> +#include <mach/kbc.h> + +#define KBC_CONTROL_0 0 +#define KBC_INT_0 4 +#define KBC_ROW_CFG0_0 8 +#define KBC_COL_CFG0_0 0x18 +#define KBC_TO_CNT_0 0x24 +#define KBC_RPT_DLY_0 0x2c +#define KBC_KP_ENT0_0 0x30 +#define KBC_KP_ENT1_0 0x34 +#define KBC_ROW0_MASK_0 0x38 + +#define res_size(res) ((res)->end - (res)->start + 1) + +struct tegra_kbc { + void __iomem *mmio; + struct input_dev *idev; + struct device *dev; + int irq; + unsigned int wake_enable_keys[KBC_MAX_ROW]; + spinlock_t lock; + unsigned int repoll_time; + struct tegra_kbc_platform_data *pdata; + int *plain_keycode; + int *fn_keycode; + struct work_struct key_repeat; + struct workqueue_struct *kbc_work_queue; + struct clk *clk; + int row_seq[KBC_MAX_ROW]; + int col_seq[KBC_MAX_COL]; + int ncols; +}; + +static int tegra_kbc_filter_keys(struct tegra_kbc *kbc, int *prows, int *pcols, + int nkey_pressed) +{ + int i = 0; + int j = 0; + int k = 0; + int filter_keys[2] = {0}; + int is_filtered = false; + int new_key_press_count = nkey_pressed; + + dev_dbg(kbc->dev, "%s\n", __func__); + + if (nkey_pressed <= 3) { + for (i = 0; i < nkey_pressed; i++) { + for (j = (i + 1); j < nkey_pressed; j++) { + if ((prows[i] + 1 == prows[j]) || + (prows[j] + 1 == prows[i])) { + for (k = j; i < (nkey_pressed - 1); + i++) { + prows[k] = prows[k+1]; + pcols[k] = pcols[k+1]; + } + nkey_pressed--; + } + if ((pcols[i] + 1 == pcols[j]) || + (pcols[j] + 1 == pcols[i])) { + for (k = j; i < (nkey_pressed - 1); + i++) { + prows[k] = prows[k+1]; + pcols[k] = pcols[k+1]; + } + nkey_pressed--; + } + } + } + return nkey_pressed; + } + + for (i = 0; i < nkey_pressed; i++) { + for (j = (i + 1); j < nkey_pressed; j++) { + if (prows[i] == prows[j]) { + for (k = 0; k < nkey_pressed; k++) { + if (k == i) + continue; + + if (pcols[i] == pcols[k]) { + filter_keys[0] = k; + is_filtered = true; + } + } + for (k = 0; k < nkey_pressed; k++) { + if (k == j) + continue; + if (pcols[j] == pcols[k]) { + filter_keys[1] = k; + is_filtered = true; + } + } + goto end; + } + } + } + +end: + if (is_filtered) { + for (i = filter_keys[0]; i < (nkey_pressed - 1); i++) { + prows[i] = prows[i+1]; + pcols[i] = pcols[i+1]; + } + new_key_press_count--; + for (i = filter_keys[1]; i < (nkey_pressed - 1); i++) { + prows[i] = prows[i+1]; + pcols[i] = pcols[i+1]; + } + new_key_press_count--; + } + nkey_pressed = new_key_press_count; + return new_key_press_count; +} + +static int tegra_kbc_keycode(struct tegra_kbc *kbc, int r, int c, bool fn_key) +{ + int code_index = kbc->row_seq[r] * kbc->ncols + kbc->col_seq[r]; + if (!fn_key) + return kbc->plain_keycode[code_index]; + else + return kbc->fn_keycode[code_index]; +} + +static void tegra_kbc_report_keys(struct tegra_kbc *kbc, int *fifo) +{ + int curr_fifo[KBC_MAX_KPRESS_EVENT]; + int rows_val[KBC_MAX_KPRESS_EVENT], cols_val[KBC_MAX_KPRESS_EVENT]; + u32 kp_ent_val[(KBC_MAX_KPRESS_EVENT + 3) / 4]; + u32 *kp_ents = kp_ent_val; + u32 kp_ent = 0; + unsigned long flags; + int i, j, valid = 0; + bool fn = false; + + dev_dbg(kbc->dev, "KBC: tegra_kbc_report_keys\n"); + + local_irq_save(flags); + for (i = 0; i < ARRAY_SIZE(kp_ent_val); i++) + kp_ent_val[i] = readl(kbc->mmio + KBC_KP_ENT0_0 + (i*4)); + local_irq_restore(flags); + + valid = 0; + for (i = 0; i < KBC_MAX_KPRESS_EVENT; i++) { + if (!(i&3)) + kp_ent = *kp_ents++; + + if (kp_ent & 0x80) { + cols_val[valid] = kp_ent & 0x7; + rows_val[valid++] = (kp_ent >> 3) & 0xf; + } + kp_ent >>= 8; + } + + if (kbc->pdata->is_filter_keys) + valid = tegra_kbc_filter_keys(kbc, rows_val, cols_val, valid); + + for (i = 0; i < valid; i++) { + int k = tegra_kbc_keycode(kbc, rows_val[i], cols_val[i], false); + if (k == KEY_FN) { + fn = true; + break; + } + } + + j = 0; + for (i = 0; i < valid; i++) { + int k = tegra_kbc_keycode(kbc, rows_val[i], cols_val[i], fn); + if (likely(k != -1)) + curr_fifo[j++] = k; + } + valid = j; + + for (i = 0; i < KBC_MAX_KPRESS_EVENT; i++) { + if (fifo[i] == -1) + continue; + for (j = 0; j < valid; j++) { + if (curr_fifo[j] == fifo[i]) { + curr_fifo[j] = -1; + break; + } + } + if (j == valid) { + input_report_key(kbc->idev, fifo[i], 0); + fifo[i] = -1; + } + } + for (j = 0; j < valid; j++) { + if (curr_fifo[j] == -1) + continue; + for (i = 0; i < KBC_MAX_KPRESS_EVENT; i++) { + if (fifo[i] == -1) + break; + } + if (i != KBC_MAX_KPRESS_EVENT) { + fifo[i] = curr_fifo[j]; + input_report_key(kbc->idev, fifo[i], 1); + } else + WARN_ON(1); + } +} + +static void tegra_kbc_key_repeat(struct work_struct *work) +{ + struct tegra_kbc *kbc; + unsigned long flags; + u32 val; + int fifo[KBC_MAX_KPRESS_EVENT]; + int i; + + kbc = container_of(work, struct tegra_kbc, key_repeat); + dev_dbg(kbc->dev, "KBC: tegra_kbc_key_repeat\n"); + + for (i = 0; i < ARRAY_SIZE(fifo); i++) + fifo[i] = -1; + + while (1) { + val = (readl(kbc->mmio + KBC_INT_0) >> 4) & 0xf; + if (!val) { + /* release any pressed keys and exit the loop */ + for (i = 0; i < ARRAY_SIZE(fifo); i++) { + if (fifo[i] == -1) + continue; + input_report_key(kbc->idev, fifo[i], 0); + } + break; + } + tegra_kbc_report_keys(kbc, fifo); + msleep((val == 1) ? kbc->repoll_time : 1); + } + + spin_lock_irqsave(&kbc->lock, flags); + val = readl(kbc->mmio + KBC_CONTROL_0); + val |= (1<<3); + writel(val, kbc->mmio + KBC_CONTROL_0); + spin_unlock_irqrestore(&kbc->lock, flags); +} + +static void tegra_kbc_close(struct input_dev *dev) +{ + struct tegra_kbc *kbc = input_get_drvdata(dev); + unsigned long flags; + u32 val; + + dev_dbg(kbc->dev, "KBC: tegra_kbc_close\n"); + + spin_lock_irqsave(&kbc->lock, flags); + val = readl(kbc->mmio + KBC_CONTROL_0); + val &= ~1; + writel(val, kbc->mmio + KBC_CONTROL_0); + spin_unlock_irqrestore(&kbc->lock, flags); + + clk_disable(kbc->clk); +} + +static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter) +{ + int i; + unsigned int rst_val; + + dev_dbg(kbc->dev, "KBC: tegra_kbc_setup_wakekeys\n"); + + BUG_ON(kbc->pdata->wake_key_cnt > KBC_MAX_KEY); + rst_val = (filter && (kbc->pdata->wake_key_cnt || + kbc->pdata->is_wake_on_any_key)) ? ~0 : 0; + + for (i = 0; i < KBC_MAX_ROW; i++) + writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4); + + if (filter) { + for (i = 0; i < KBC_MAX_ROW; i++) { + if (kbc->wake_enable_keys[i] != rst_val) + writel(kbc->wake_enable_keys[i], + kbc->mmio + KBC_ROW0_MASK_0 + i * 4); + } + } +} + +static void tegra_kbc_config_pins(struct tegra_kbc *kbc) +{ + const struct tegra_kbc_platform_data *pdata = kbc->pdata; + int i; + unsigned int row_config[4]; + unsigned int col_config[3]; + + dev_dbg(kbc->dev, "KBC: tegra_kbc_config_pins\n"); + for (i = 0; i < 4; i++) + row_config[i] = 0; + for (i = 0; i < 3; i++) + col_config[i] = 0; + + for (i = 0; i < KBC_MAX_GPIO; i++) { + u32 r_shift = 5 * (pdata->pin_cfg[i].num % 6); + u32 c_shift = 4 * (pdata->pin_cfg[i].num % 8); + u32 r_mask = 0x1f << r_shift; + u32 c_mask = 0xf << c_shift; + u32 index; + + if (pdata->pin_cfg[i].pin_type == kbc_pin_unused) + continue; + + if (pdata->pin_cfg[i].pin_type == kbc_pin_row) { + index = pdata->pin_cfg[i].num / 6; + row_config[index] &= ~r_mask; + row_config[index] |= + ((pdata->pin_cfg[i].num << 1) | 1) << r_shift; + } else { + index = (pdata->pin_cfg[i].num + KBC_MAX_ROW) / 8; + col_config[index] &= ~c_mask; + col_config[index] |= + ((pdata->pin_cfg[i].num << 1) | 1) << c_shift; + } + } + for (i = 0; i < 4; i++) { + u32 r_offs = i * 4 + KBC_ROW_CFG0_0; + writel(row_config[i], kbc->mmio + r_offs); + } + for (i = 0; i < 3; i++) { + u32 c_offs = i * 4 + KBC_COL_CFG0_0; + writel(col_config[i], kbc->mmio + c_offs); + } +} + +static int tegra_kbc_open(struct input_dev *dev) +{ + struct tegra_kbc *kbc = input_get_drvdata(dev); + unsigned long flags; + u32 val = 0; + + dev_dbg(kbc->dev, "KBC: tegra_kbc_open\n"); + + clk_enable(kbc->clk); + + /* Reset the KBC controller to clear all previous status.*/ + tegra_periph_reset_assert(kbc->clk); + udelay(100); + tegra_periph_reset_deassert(kbc->clk); + udelay(100); + + tegra_kbc_config_pins(kbc); + tegra_kbc_setup_wakekeys(kbc, false); + + writel(kbc->pdata->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0); + + val = kbc->pdata->debounce_cnt << 4; + val |= 1<<14; /* fifo interrupt threshold = 1 entry */ + val |= 1<<3; /* interrupt on FIFO threshold reached */ + val |= 1; /* enable */ + writel(val, kbc->mmio + KBC_CONTROL_0); + + /* Bit 19:0 is for scan timeout count */ + kbc->pdata->scan_timeout_cnt &= 0xFFFFF; + writel(kbc->pdata->scan_timeout_cnt, kbc->mmio + KBC_TO_CNT_0); + + /* atomically clear out any remaining entries in the key FIFO + * and enable keyboard interrupts */ + spin_lock_irqsave(&kbc->lock, flags); + + while (1) { + val = readl(kbc->mmio + KBC_INT_0); + val >>= 4; + if (val) { + val = readl(kbc->mmio + KBC_KP_ENT0_0); + val = readl(kbc->mmio + KBC_KP_ENT1_0); + } else { + break; + } + } + writel(0x7, kbc->mmio + KBC_INT_0); + spin_unlock_irqrestore(&kbc->lock, flags); + return 0; +} + +static irqreturn_t tegra_kbc_isr(int irq, void *args) +{ + struct tegra_kbc *kbc = args; + u32 val, ctl; + + dev_dbg(kbc->dev, "KBC: tegra_kbc_isr\n"); + + /* until all keys are released, defer further processing to + * the polling loop in tegra_kbc_key_repeat */ + ctl = readl(kbc->mmio + KBC_CONTROL_0); + ctl &= ~(1<<3); + writel(ctl, kbc->mmio + KBC_CONTROL_0); + + /* quickly bail out & reenable interrupts if the interrupt source + * wasn't fifo count threshold */ + val = readl(kbc->mmio + KBC_INT_0); + writel(val, kbc->mmio + KBC_INT_0); + + if (!(val & (1<<2))) { + ctl |= 1<<3; + writel(ctl, kbc->mmio + KBC_CONTROL_0); + return IRQ_HANDLED; + } + + queue_work(kbc->kbc_work_queue, &kbc->key_repeat); + return IRQ_HANDLED; +} + +static int __devinit tegra_kbc_probe(struct platform_device *pdev) +{ + struct tegra_kbc *kbc; + struct tegra_kbc_platform_data *pdata = pdev->dev.platform_data; + struct resource *res; + int irq; + int err; + int rows[KBC_MAX_ROW]; + int cols[KBC_MAX_COL]; + int i, j; + int nr = 0; + int nc = 0; + char name[64]; + + dev_dbg(&pdev->dev, "KBC: tegra_kbc_probe\n"); + + if (!pdata) + return -EINVAL; + + /* Validate the data entry */ + if (!pdata->plain_keycode) { + dev_err(&pdev->dev, "No key codes\n"); + return -EINVAL; + } + + for (i = 0; i < KBC_MAX_GPIO; i++) { + if ((pdata->pin_cfg[i].pin_type == kbc_pin_row) && + (pdata->pin_cfg[i].num >= KBC_MAX_ROW)) { + dev_err(&pdev->dev, "Invalid row number\n"); + return -EINVAL; + } else if ((pdata->pin_cfg[i].pin_type == kbc_pin_col) && + (pdata->pin_cfg[i].num >= KBC_MAX_COL)) { + dev_err(&pdev->dev, "Invalid column number\n"); + return -EINVAL; + } + } + + kbc = kzalloc(sizeof(*kbc), GFP_KERNEL); + if (!kbc) + return -ENOMEM; + + kbc->pdata = pdata; + kbc->irq = -EINVAL; + + memset(rows, 0, sizeof(rows)); + memset(cols, 0, sizeof(cols)); + + kbc->idev = input_allocate_device(); + if (!kbc->idev) { + err = -ENOMEM; + goto fail; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get I/O memory\n"); + err = -ENXIO; + goto fail; + } + res = request_mem_region(res->start, res_size(res), pdev->name); + if (!res) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + err = -EBUSY; + goto fail; + } + kbc->mmio = ioremap(res->start, res_size(res)); + if (!kbc->mmio) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + err = -ENXIO; + goto fail; + } + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get keypad IRQ\n"); + err = -ENXIO; + goto fail; + } + kbc->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR_OR_NULL(kbc->clk)) { + dev_err(&pdev->dev, "failed to get keypad clock\n"); + err = (kbc->clk) ? PTR_ERR(kbc->clk) : -ENODEV; + kbc->clk = NULL; + goto fail; + } + + platform_set_drvdata(pdev, kbc); + + kbc->dev = &pdev->dev; + kbc->idev->name = pdev->name; + input_set_drvdata(kbc->idev, kbc); + kbc->idev->id.bustype = BUS_HOST; + kbc->idev->open = tegra_kbc_open; + kbc->idev->close = tegra_kbc_close; + kbc->idev->dev.parent = &pdev->dev; + spin_lock_init(&kbc->lock); + + for (i = 0; i < KBC_MAX_GPIO; i++) { + if (pdata->pin_cfg[i].pin_type == kbc_pin_row) { + rows[pdata->pin_cfg[i].num] = 1; + kbc->row_seq[pdata->pin_cfg[i].num] = nr++; + } else if (pdata->pin_cfg[i].pin_type == kbc_pin_col) { + cols[pdata->pin_cfg[i].num] = 1; + kbc->col_seq[pdata->pin_cfg[i].num] = nc++; + } + } + kbc->ncols = nc; + + for (i = 0; i < pdata->wake_key_cnt; i++) + kbc->wake_enable_keys[i] = ~0u; + + for (i = 0; i < pdata->wake_key_cnt; i++) + kbc->wake_enable_keys[kbc->pdata->wake_cfg[i].row] &= + ~(1 << kbc->pdata->wake_cfg[i].col); + + pdata->debounce_cnt = min_t(unsigned int, pdata->debounce_cnt, 0x3fful); + kbc->repoll_time = 5 + (16+pdata->debounce_cnt)*nr + pdata->repeat_cnt; + kbc->repoll_time = (kbc->repoll_time + 31) / 32; + + kbc->plain_keycode = pdata->plain_keycode; + kbc->fn_keycode = pdata->fn_keycode; + + kbc->idev->evbit[0] = BIT_MASK(EV_KEY); + for (i = 0; i < KBC_MAX_ROW; i++) { + if (!rows[i]) + continue; + for (j = 0; j < KBC_MAX_COL; j++) { + int keycode; + if (!cols[j]) + continue; + keycode = tegra_kbc_keycode(kbc, i, j, false); + if (keycode == KEY_RESERVED) + continue; + set_bit(keycode, kbc->idev->keybit); + } + } + + /* create the workqueue for the kbc path */ + snprintf(name, sizeof(name), "tegra-kbc"); + kbc->kbc_work_queue = create_singlethread_workqueue(name); + if (kbc->kbc_work_queue == NULL) { + dev_err(&pdev->dev, "Failed to create work queue\n"); + err = -ENODEV; + goto fail; + } + + /* keycode FIFO needs to be read atomically; leave local + * interrupts disabled when handling KBC interrupt */ + INIT_WORK(&kbc->key_repeat, tegra_kbc_key_repeat); + + err = request_irq(irq, tegra_kbc_isr, IRQF_DISABLED, pdev->name, kbc); + if (err) { + dev_err(&pdev->dev, "failed to request keypad IRQ\n"); + goto fail; + } + kbc->irq = irq; + + err = input_register_device(kbc->idev); + if (err) { + dev_err(&pdev->dev, "failed to register input device\n"); + goto fail; + } + + device_init_wakeup(&pdev->dev, 1); + return 0; + +fail: + if (kbc->irq >= 0) + free_irq(kbc->irq, pdev); + if (kbc->idev) + input_free_device(kbc->idev); + if (kbc->clk) + clk_put(kbc->clk); + if (kbc->mmio) + iounmap(kbc->mmio); + if (kbc->kbc_work_queue) + destroy_workqueue(kbc->kbc_work_queue); + kfree(kbc); + + return err; +} + +static int __devexit tegra_kbc_remove(struct platform_device *pdev) +{ + struct tegra_kbc *kbc = platform_get_drvdata(pdev); + struct resource *res; + + dev_dbg(kbc->dev, "KBC: tegra_kbc_remove\n"); + + free_irq(kbc->irq, pdev); + clk_disable(kbc->clk); + clk_put(kbc->clk); + + input_unregister_device(kbc->idev); + input_free_device(kbc->idev); + iounmap(kbc->mmio); + destroy_workqueue(kbc->kbc_work_queue); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, res_size(res)); + + kfree(kbc); + return 0; +} + +#ifdef CONFIG_PM +static int tegra_kbc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct tegra_kbc *kbc = platform_get_drvdata(pdev); + + dev_dbg(&pdev->dev, "KBC: tegra_kbc_suspend\n"); + if (device_may_wakeup(&pdev->dev) && + (kbc->pdata->is_wake_on_any_key || kbc->pdata->wake_key_cnt)) { + tegra_kbc_setup_wakekeys(kbc, true); + enable_irq_wake(kbc->irq); + /* Forcefully clear the interrupt status */ + writel(0x7, kbc->mmio + KBC_INT_0); + msleep(30); + } else { + tegra_kbc_close(kbc->idev); + } + return 0; +} + +static int tegra_kbc_resume(struct platform_device *pdev) +{ + struct tegra_kbc *kbc = platform_get_drvdata(pdev); + + dev_dbg(&pdev->dev, "KBC: tegra_kbc_resume\n"); + + if (device_may_wakeup(&pdev->dev)) { + disable_irq_wake(kbc->irq); + tegra_kbc_setup_wakekeys(kbc, false); + } else if (kbc->idev->users) + return tegra_kbc_open(kbc->idev); + + return 0; +} +#endif + +static struct platform_driver tegra_kbc_driver = { + .probe = tegra_kbc_probe, + .remove = tegra_kbc_remove, +#ifdef CONFIG_PM + .suspend = tegra_kbc_suspend, + .resume = tegra_kbc_resume, +#endif + .driver = { + .name = "tegra-kbc" + } +}; + +static int __devinit tegra_kbc_init(void) +{ + pr_debug("KBC: tegra_kbc_init\n"); + return platform_driver_register(&tegra_kbc_driver); +} + +static void __exit tegra_kbc_exit(void) +{ + pr_debug("KBC: tegra_kbc_exit\n"); + platform_driver_unregister(&tegra_kbc_driver); +} + +module_init(tegra_kbc_init); +module_exit(tegra_kbc_exit); + +MODULE_DESCRIPTION("Tegra matrix keyboard controller driver"); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 263471a905f7..37f843e95af0 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -454,4 +454,20 @@ config INPUT_ADXL34X_SPI To compile this driver as a module, choose M here: the module will be called adxl34x-spi. +config INPUT_ALPS_GPIO_SCROLLWHEEL + tristate "Alps GPIO Scrollwheel" + depends on GENERIC_GPIO + help + This driver implements support for Alps SRBE + ScrollWheel connected to GPIO pins of various + CPUs (and some other chips). + + Say Y here if your device has ScrollWheel connected + directly to such GPIO pins. Your board-specific + setup logic must also provide a platform device, + with configuration data saying which GPIOs are used. + + To compile this driver as a module, choose M here: the + module will be called alps_gpio_scrollwheel. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 7ac4ca759999..bc875ba892da 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_INPUT_AD714X_SPI) += ad714x-spi.o obj-$(CONFIG_INPUT_ADXL34X) += adxl34x.o obj-$(CONFIG_INPUT_ADXL34X_I2C) += adxl34x-i2c.o obj-$(CONFIG_INPUT_ADXL34X_SPI) += adxl34x-spi.o +obj-$(CONFIG_INPUT_ALPS_GPIO_SCROLLWHEEL) += alps_gpio_scrollwheel.o obj-$(CONFIG_INPUT_APANEL) += apanel.o obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o diff --git a/drivers/input/misc/alps_gpio_scrollwheel.c b/drivers/input/misc/alps_gpio_scrollwheel.c new file mode 100644 index 000000000000..4a789267c475 --- /dev/null +++ b/drivers/input/misc/alps_gpio_scrollwheel.c @@ -0,0 +1,428 @@ +/* + * kernel/drivers/input/misc/alps_gpio_scrollwheel.c + * + * Copyright (c) 2010, NVIDIA Corporation. + * + * Driver for ScrollWheel on GPIO lines capable of generating interrupts. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/sched.h> +#include <linux/pm.h> +#include <linux/slab.h> +#include <linux/sysctl.h> +#include <linux/proc_fs.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/gpio_scrollwheel.h> +#include <linux/workqueue.h> +#include <linux/gpio.h> + +struct scrollwheel_button_data { + struct gpio_scrollwheel_button *button; + struct input_dev *input; + struct timer_list timer; + struct work_struct work; + int timer_debounce; /* in msecs */ + int rotgpio; + bool disabled; +}; + +struct gpio_scrollwheel_drvdata { + struct input_dev *input; + struct mutex disable_lock; + unsigned int n_buttons; + int (*enable)(struct device *dev); + void (*disable)(struct device *dev); + struct scrollwheel_button_data data[0]; +}; + +static void scrollwheel_report_key(struct scrollwheel_button_data *bdata) +{ + struct gpio_scrollwheel_button *button = bdata->button; + struct input_dev *input = bdata->input; + int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ \ + button->active_low; + int state2 = 0; + + switch (button->pinaction) { + case GPIO_SCROLLWHEEL_PIN_PRESS: + input_report_key(input, KEY_ENTER, 1); + input_report_key(input, KEY_ENTER, 0); + input_sync(input); + break; + + case GPIO_SCROLLWHEEL_PIN_ROT1: + case GPIO_SCROLLWHEEL_PIN_ROT2: + state2 = (gpio_get_value(bdata->rotgpio) ? 1 : 0) \ + ^ button->active_low; + if (state != state2) { + input_report_key(input, KEY_DOWN, 1); + input_report_key(input, KEY_DOWN, 0); + } else { + input_report_key(input, KEY_UP, 1); + input_report_key(input, KEY_UP, 0); + } + input_sync(input); + break; + + default: + pr_err("%s:Line=%d, Invalid Pinaction\n", __func__, __LINE__); + } +} + +static void scrollwheel_work_func(struct work_struct *work) +{ + struct scrollwheel_button_data *bdata = + container_of(work, struct scrollwheel_button_data, work); + + scrollwheel_report_key(bdata); +} + +static void scrollwheel_timer(unsigned long _data) +{ + struct scrollwheel_button_data *data = \ + (struct scrollwheel_button_data *)_data; + + schedule_work(&data->work); +} + +static irqreturn_t scrollwheel_isr(int irq, void *dev_id) +{ + struct scrollwheel_button_data *bdata = dev_id; + struct gpio_scrollwheel_button *button = bdata->button; + + BUG_ON(irq != gpio_to_irq(button->gpio)); + + if (bdata->timer_debounce) + mod_timer(&bdata->timer, + jiffies + msecs_to_jiffies(bdata->timer_debounce)); + else + schedule_work(&bdata->work); + + return IRQ_HANDLED; +} + +static int __devinit gpio_scrollwheel_setup_key(struct platform_device *pdev, + struct scrollwheel_button_data *bdata, + struct gpio_scrollwheel_button *button) +{ + char *desc = button->desc ? button->desc : "gpio_scrollwheel"; + struct device *dev = &pdev->dev; + unsigned long irqflags; + int irq, error; + + setup_timer(&bdata->timer, scrollwheel_timer, (unsigned long)bdata); + INIT_WORK(&bdata->work, scrollwheel_work_func); + + error = gpio_request(button->gpio, desc); + if (error < 0) { + dev_err(dev, "failed to request GPIO %d, error %d\n", + button->gpio, error); + return error; + } + + error = gpio_direction_input(button->gpio); + if (error < 0) { + dev_err(dev, "failed to configure" + " direction for GPIO %d, error %d\n", + button->gpio, error); + goto fail; + } + + if (button->debounce_interval) { + error = gpio_set_debounce(button->gpio, + button->debounce_interval * 1000); + /* use timer if gpiolib doesn't provide debounce */ + if (error < 0) + bdata->timer_debounce = button->debounce_interval; + } + + irq = gpio_to_irq(button->gpio); + if (irq < 0) { + error = irq; + dev_err(dev, "Unable to get irq no for GPIO %d, error %d\n", + button->gpio, error); + goto fail; + } + + irqflags = IRQF_TRIGGER_FALLING; + + error = request_irq(irq, scrollwheel_isr, irqflags, desc, bdata); + if (error) { + dev_err(dev, "Unable to claim irq %d; error %d\n", + irq, error); + goto fail; + } + + return 0; + +fail: + return error; +} + +static int gpio_scrollwheel_open(struct input_dev *input) +{ + struct gpio_scrollwheel_drvdata *ddata = input_get_drvdata(input); + + return ddata->enable ? ddata->enable(input->dev.parent) : 0; +} + +static void gpio_scrollwheel_close(struct input_dev *input) +{ + struct gpio_scrollwheel_drvdata *ddata = input_get_drvdata(input); + + if (ddata->disable) + ddata->disable(input->dev.parent); +} + +static int __devinit gpio_scrollwheel_probe(struct platform_device *pdev) +{ + struct gpio_scrollwheel_platform_data *pdata = pdev->dev.platform_data; + struct gpio_scrollwheel_drvdata *ddata; + struct device *dev = &pdev->dev; + struct input_dev *input; + int i, error; + + ddata = kzalloc(sizeof(struct gpio_scrollwheel_drvdata) + + pdata->nbuttons * sizeof(struct scrollwheel_button_data), + GFP_KERNEL); + if (ddata == NULL) { + dev_err(dev, "failed to allocate memory\n"); + error = -ENOMEM; + return error; + } + + input = input_allocate_device(); + if (input == NULL) { + dev_err(dev, "failed to allocate input device\n"); + error = -ENOMEM; + kfree(ddata); + return error; + } + + ddata->input = input; + ddata->n_buttons = pdata->nbuttons; + ddata->enable = pdata->enable; + ddata->disable = pdata->disable; + mutex_init(&ddata->disable_lock); + + platform_set_drvdata(pdev, ddata); + input_set_drvdata(input, ddata); + + input->name = pdev->name; + input->phys = "gpio-scrollwheel/input0"; + input->dev.parent = &pdev->dev; + input->open = gpio_scrollwheel_open; + input->close = gpio_scrollwheel_close; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + /* Enable auto repeat feature of Linux input subsystem */ + if (pdata->rep) + __set_bit(EV_REP, input->evbit); + + for (i = 0; i < pdata->nbuttons; i++) { + struct gpio_scrollwheel_button *button = &pdata->buttons[i]; + struct scrollwheel_button_data *bdata = &ddata->data[i]; + + bdata->input = input; + bdata->button = button; + + if (button->pinaction == GPIO_SCROLLWHEEL_PIN_PRESS || + button->pinaction == GPIO_SCROLLWHEEL_PIN_ROT1) { + error = gpio_scrollwheel_setup_key(pdev, bdata, button); + if (error) + goto fail; + } else { + if (button->pinaction == GPIO_SCROLLWHEEL_PIN_ONOFF) { + gpio_request(button->gpio, button->desc); + gpio_direction_output(button->gpio, 0); + } + + if (button->pinaction == GPIO_SCROLLWHEEL_PIN_ROT2) { + gpio_request(button->gpio, button->desc); + gpio_direction_input(button->gpio); + /* Save rot2 gpio number in rot1 context */ + ddata->data[2].rotgpio = button->gpio; + } + } + } + + /* set input capability */ + __set_bit(EV_KEY, input->evbit); + __set_bit(KEY_ENTER, input->keybit); + __set_bit(KEY_UP, input->keybit); + __set_bit(KEY_DOWN, input->keybit); + + error = input_register_device(input); + if (error) { + dev_err(dev, "Unable to register input device, error: %d\n", + error); + goto fail; + } + + input_sync(input); + + return 0; + +fail: + while (--i >= 0) { + if (pdata->buttons[i].pinaction == GPIO_SCROLLWHEEL_PIN_PRESS || + pdata->buttons[i].pinaction == GPIO_SCROLLWHEEL_PIN_ROT1) { + free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]); + if (ddata->data[i].timer_debounce) + del_timer_sync(&ddata->data[i].timer); + cancel_work_sync(&ddata->data[i].work); + } + gpio_free(pdata->buttons[i].gpio); + } + + platform_set_drvdata(pdev, NULL); + input_free_device(input); + kfree(ddata); + return error; +} + +static int __devexit gpio_scrollwheel_remove(struct platform_device *pdev) +{ + struct gpio_scrollwheel_platform_data *pdata = pdev->dev.platform_data; + struct gpio_scrollwheel_drvdata *ddata = platform_get_drvdata(pdev); + struct input_dev *input = ddata->input; + int i; + + for (i = 0; i < pdata->nbuttons; i++) { + if (pdata->buttons[i].pinaction == GPIO_SCROLLWHEEL_PIN_PRESS || + pdata->buttons[i].pinaction == GPIO_SCROLLWHEEL_PIN_ROT1) { + int irq = gpio_to_irq(pdata->buttons[i].gpio); + free_irq(irq, &ddata->data[i]); + if (ddata->data[i].timer_debounce) + del_timer_sync(&ddata->data[i].timer); + cancel_work_sync(&ddata->data[i].work); + } + gpio_free(pdata->buttons[i].gpio); + } + + input_unregister_device(input); + + return 0; +} + + +#ifdef CONFIG_PM +static int gpio_scrollwheel_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gpio_scrollwheel_platform_data *pdata = pdev->dev.platform_data; + struct gpio_scrollwheel_drvdata *ddata = platform_get_drvdata(pdev); + int i, irq; + + for (i = 0; i < pdata->nbuttons; i++) { + if (pdata->buttons[i].pinaction == GPIO_SCROLLWHEEL_PIN_PRESS || + pdata->buttons[i].pinaction == GPIO_SCROLLWHEEL_PIN_ROT1) { + irq = gpio_to_irq(pdata->buttons[i].gpio); + disable_irq(irq); + if (ddata->data[i].timer_debounce) + del_timer_sync(&ddata->data[i].timer); + cancel_work_sync(&ddata->data[i].work); + } else { + if (pdata->buttons[i].pinaction == GPIO_SCROLLWHEEL_PIN_ONOFF) + gpio_direction_output(pdata->buttons[i].gpio, 1); + else { + irq = gpio_to_irq(pdata->buttons[i].gpio); + disable_irq(irq); + } + } + } + return 0; +} + +static int gpio_scrollwheel_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gpio_scrollwheel_platform_data *pdata = pdev->dev.platform_data; + struct gpio_scrollwheel_drvdata *ddata = platform_get_drvdata(pdev); + int i, irq; + + for (i = 0; i < pdata->nbuttons; i++) { + if (pdata->buttons[i].pinaction == GPIO_SCROLLWHEEL_PIN_PRESS || + pdata->buttons[i].pinaction == GPIO_SCROLLWHEEL_PIN_ROT1) { + irq = gpio_to_irq(pdata->buttons[i].gpio); + enable_irq(irq); + if (ddata->data[i].timer_debounce) + setup_timer(&ddata->data[i].timer,\ + scrollwheel_timer, (unsigned long)&ddata->data[i]); + + INIT_WORK(&ddata->data[i].work, scrollwheel_work_func); + } else { + if (pdata->buttons[i].pinaction == GPIO_SCROLLWHEEL_PIN_ONOFF) + gpio_direction_output(pdata->buttons[i].gpio, 0); + else { + irq = gpio_to_irq(pdata->buttons[i].gpio); + enable_irq(irq); + } + } + } + + return 0; +} + +static const struct dev_pm_ops gpio_scrollwheel_pm_ops = { + .suspend = gpio_scrollwheel_suspend, + .resume = gpio_scrollwheel_resume, +}; +#endif + +static struct platform_driver gpio_scrollwheel_device_driver = { + .probe = gpio_scrollwheel_probe, + .remove = __devexit_p(gpio_scrollwheel_remove), + .driver = { + .name = "alps-gpio-scrollwheel", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &gpio_scrollwheel_pm_ops, +#endif + } +}; + +static int __init gpio_scrollwheel_init(void) +{ + return platform_driver_register(&gpio_scrollwheel_device_driver); +} + +static void __exit gpio_scrollwheel_exit(void) +{ + platform_driver_unregister(&gpio_scrollwheel_device_driver); +} + +module_init(gpio_scrollwheel_init); +module_exit(gpio_scrollwheel_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("NVIDIA Corporation"); +MODULE_DESCRIPTION("Alps SRBE ScrollWheel driver"); + +MODULE_ALIAS("platform:alps-gpio-scrollwheel"); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index d50fd5603604..f7c25b2b7ed6 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -383,6 +383,13 @@ config TOUCHSCREEN_ATMEL_TSADCC To compile this driver as a module, choose M here: the module will be called atmel_tsadcc. +config TOUCHSCREEN_ATMEL_MT_T9 + tristate "Atmel Touchscreen Interface" + depends on I2C + help + To compile this driver as a module, choose M here: the + module will be called atmel_tsadcc. + config TOUCHSCREEN_UCB1400 tristate "Philips UCB1400 touchscreen" depends on AC97_BUS diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 93e641bc320e..b588efc90bf8 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C) += ad7879-i2c.o obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o +obj-$(CONFIG_TOUCHSCREEN_ATMEL_MT_T9) += atmel_maxtouch.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o diff --git a/drivers/input/touchscreen/atmel_maxtouch.c b/drivers/input/touchscreen/atmel_maxtouch.c new file mode 100755 index 000000000000..44ef974575d1 --- /dev/null +++ b/drivers/input/touchscreen/atmel_maxtouch.c @@ -0,0 +1,2056 @@ +/* + * Atmel maXTouch Touchscreen Controller + * + * + * Copyright (C) 2010 Atmel Corporation + * Copyright (C) 2009 Raphael Derosso Pereira <raphaelpereira@gmail.com> + * + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/debugfs.h> +#include <linux/cdev.h> +#include <linux/mutex.h> +#include <linux/slab.h> + +#include <linux/uaccess.h> + +#include <linux/i2c/atmel_maxtouch.h> + +/* + * This is a driver for the Atmel maXTouch Object Protocol + * + * When the driver is loaded, mxt_init is called. + * mxt_driver registers the "mxt_driver" structure in the i2c subsystem + * The mxt_idtable.name string allows the board support to associate + * the driver with its own data. + * + * The i2c subsystem will call the mxt_driver.probe == mxt_probe + * to detect the device. + * mxt_probe will reset the maXTouch device, and then + * determine the capabilities of the I2C peripheral in the + * host processor (needs to support BYTE transfers) + * + * If OK; mxt_probe will try to identify which maXTouch device it is + * by calling mxt_identify. + * + * If a known device is found, a linux input device is initialized + * the "mxt" device data structure is allocated, + * as well as an input device structure "mxt->input" + * "mxt->client" is provided as a parameter to mxt_probe. + * + * mxt_read_object_table is called to determine which objects + * are present in the device, and to determine their adresses. + * + * + * Addressing an object: + * + * The object is located at a 16-bit address in the object address space. + * + * The address is provided through an object descriptor table in the beginning + * of the object address space. This address can change between firmware + * revisions, so it's important that the driver will make no assumptions + * about addresses but instead reads the object table and gets the correct + * addresses there. + * + * Each object type can have several instances, and the number of + * instances is available in the object table as well. + * + * The base address of the first instance of an object is stored in + * "mxt->object_table[object_type].chip_addr", + * This is indexed by the object type and allows direct access to the + * first instance of an object. + * + * Each instance of an object is assigned a "Report Id" uniquely identifying + * this instance. Information about this instance is available in the + * "mxt->report_id" variable, which is a table indexed by the "Report Id". + * + * The maXTouch object protocol supports adding a checksum to messages. + * By setting the most significant bit of the maXTouch address, + * an 8 bit checksum is added to all writes. + * + * + * How to use driver. + * ----------------- + * Example: + * In arch/avr32/boards/atstk1000/atstk1002.c + * an "i2c_board_info" descriptor is declared. + * This contains info about which driver ("mXT224"), + * which i2c address and which pin for CHG interrupts are used. + * + * In the "atstk1002_init" routine, "i2c_register_board_info" is invoked + * with this information. Also, the I/O pins are configured, and the I2C + * controller registered is on the application processor. + * + * + */ + +static int debug = NO_DEBUG; +static int comms; +module_param(debug, int, 0644); +module_param(comms, int, 0644); + +MODULE_PARM_DESC(debug, "Activate debugging output"); +MODULE_PARM_DESC(comms, "Select communications mode"); + +/* Device Info descriptor */ +/* Parsed from maXTouch "Id information" inside device */ +struct mxt_device_info { + u8 family_id; + u8 variant_id; + u8 major; + u8 minor; + u8 build; + u8 num_objs; + u8 x_size; + u8 y_size; + char family_name[16]; /* Family name */ + char variant_name[16]; /* Variant name */ + u16 num_nodes; /* Number of sensor nodes */ +}; + +/* object descriptor table, parsed from maXTouch "object table" */ +struct mxt_object { + u16 chip_addr; + u8 type; + u8 size; + u8 instances; + u8 num_report_ids; +}; + +/* Mapping from report id to object type and instance */ +struct report_id_map { + u8 object; + u8 instance; +/* + * This is the first report ID belonging to object. It enables us to + * find out easily the touch number: each touch has different report + * ID (which are assigned to touches in increasing order). By + * subtracting the first report ID from current, we get the touch + * number. + */ + u8 first_rid; +}; + +/* Driver datastructure */ +struct mxt_data { + struct i2c_client *client; + struct input_dev *input; + char phys_name[32]; + int irq; + + u16 last_read_addr; + bool new_msgs; + u8 *last_message; + + int valid_irq_counter; + int invalid_irq_counter; + int irq_counter; + int message_counter; + int read_fail_counter; + + int bytes_to_read; + + struct delayed_work dwork; + u8 xpos_format; + u8 ypos_format; + + u8 numtouch; + + struct mxt_device_info device_info; + + u32 info_block_crc; + u32 configuration_crc; + u16 report_id_count; + struct report_id_map *rid_map; + struct mxt_object *object_table; + + u16 msg_proc_addr; + u8 message_size; + + u16 max_x_val; + u16 max_y_val; + + void (*init_hw) (void); + void (*exit_hw) (void); + u8(*valid_interrupt) (void); + u8(*read_chg) (void); + + /* debugfs variables */ + struct dentry *debug_dir; + int current_debug_datap; + + struct mutex debug_mutex; + u16 *debug_data; + + /* Character device variables */ + struct cdev cdev; + struct cdev cdev_messages; /* 2nd Char dev for messages */ + dev_t dev_num; + struct class *mxt_class; + + u16 address_pointer; + bool valid_ap; + + /* Message buffer & pointers */ + char *messages; + int msg_buffer_startp, msg_buffer_endp; + /* Put only non-touch messages to buffer if this is set */ + char nontouch_msg_only; + struct mutex msg_mutex; +}; + +#define I2C_RETRY_COUNT 5 +#define I2C_PAYLOAD_SIZE 254 + +/* Returns the start address of object in mXT memory. */ +#define MXT_BASE_ADDR(object_type, mxt) \ + get_object_address(object_type, 0, mxt->object_table, \ + mxt->device_info.num_objs) + +/* Maps a report ID to an object type (object type number). */ +#define REPORT_ID_TO_OBJECT(rid, mxt) \ + (((rid) == 0xff) ? 0 : mxt->rid_map[rid].object) + +/* Maps a report ID to an object type (string). */ +#define REPORT_ID_TO_OBJECT_NAME(rid, mxt) \ + object_type_name[REPORT_ID_TO_OBJECT(rid, mxt)] + +/* Returns non-zero if given object is a touch object */ +#define IS_TOUCH_OBJECT(object) \ + ((object == MXT_TOUCH_MULTITOUCHSCREEN_T9) || \ + (object == MXT_TOUCH_KEYARRAY_T15) || \ + (object == MXT_TOUCH_PROXIMITY_T23) || \ + (object == MXT_TOUCH_SINGLETOUCHSCREEN_T10) || \ + (object == MXT_TOUCH_XSLIDER_T11) || \ + (object == MXT_TOUCH_YSLIDER_T12) || \ + (object == MXT_TOUCH_XWHEEL_T13) || \ + (object == MXT_TOUCH_YWHEEL_T14) || \ + (object == MXT_TOUCH_KEYSET_T31) || \ + (object == MXT_TOUCH_XSLIDERSET_T32) ? 1 : 0) + +#define mxt_debug(level, ...) \ + do { \ + if (debug >= (level)) \ + pr_debug(__VA_ARGS__); \ + } while (0) + +static const u8 *object_type_name[] = { + [0] = "Reserved", + [5] = "GEN_MESSAGEPROCESSOR_T5", + [6] = "GEN_COMMANDPROCESSOR_T6", + [7] = "GEN_POWERCONFIG_T7", + [8] = "GEN_ACQUIRECONFIG_T8", + [9] = "TOUCH_MULTITOUCHSCREEN_T9", + [15] = "TOUCH_KEYARRAY_T15", + [17] = "SPT_COMMSCONFIG_T18", + [19] = "SPT_GPIOPWM_T19", + [20] = "PROCI_GRIPFACESUPPRESSION_T20", + [22] = "PROCG_NOISESUPPRESSION_T22", + [23] = "TOUCH_PROXIMITY_T23", + [24] = "PROCI_ONETOUCHGESTUREPROCESSOR_T24", + [25] = "SPT_SELFTEST_T25", + [27] = "PROCI_TWOTOUCHGESTUREPROCESSOR_T27", + [28] = "SPT_CTECONFIG_T28", + [37] = "DEBUG_DIAGNOSTICS_T37", + [38] = "SPT_USER_DATA_T38", + [40] = "PROCI_GRIPSUPPRESSION_T40", + [41] = "PROCI_PALMSUPPRESSION_T41", + [42] = "PROCI_FACESUPPRESSION_T42", + [43] = "SPT_DIGITIZER_T43", + [44] = "SPT_MESSAGECOUNT_T44", +}; + +static u16 get_object_address(uint8_t object_type, + uint8_t instance, + struct mxt_object *object_table, int max_objs); + +static int mxt_write_ap(struct mxt_data *mxt, u16 ap); + +static int mxt_read_block_wo_addr(struct i2c_client *client, + u16 length, u8 *value); + +ssize_t debug_data_read(struct mxt_data *mxt, char *buf, size_t count, + loff_t *ppos, u8 debug_command) +{ + int i; + u16 *data; + u16 diagnostics_reg; + int offset = 0; + int size; + int read_size; + int error; + char *buf_start; + u16 debug_data_addr; + u16 page_address; + u8 page; + u8 debug_command_reg; + + data = mxt->debug_data; + if (data == NULL) + return -EIO; + + /* If first read after open, read all data to buffer. */ + if (mxt->current_debug_datap == 0) { + + diagnostics_reg = MXT_BASE_ADDR(MXT_GEN_COMMANDPROCESSOR_T6, + mxt) + MXT_ADR_T6_DIAGNOSTIC; + if (count > (mxt->device_info.num_nodes * 2)) + count = mxt->device_info.num_nodes; + + debug_data_addr = MXT_BASE_ADDR(MXT_DEBUG_DIAGNOSTIC_T37, mxt) + + MXT_ADR_T37_DATA; + page_address = MXT_BASE_ADDR(MXT_DEBUG_DIAGNOSTIC_T37, mxt) + + MXT_ADR_T37_PAGE; + error = mxt_read_block(mxt->client, page_address, 1, &page); + if (error < 0) + return error; + mxt_debug(DEBUG_TRACE, "debug data page = %d\n", page); + while (page != 0) { + error = mxt_write_byte(mxt->client, + diagnostics_reg, + MXT_CMD_T6_PAGE_DOWN); + if (error < 0) + return error; + /* Wait for command to be handled; when it has, the + register will be cleared. */ + debug_command_reg = 1; + while (debug_command_reg != 0) { + error = mxt_read_block(mxt->client, + diagnostics_reg, 1, + &debug_command_reg); + if (error < 0) + return error; + mxt_debug(DEBUG_TRACE, + "Waiting for debug diag command " + "to propagate...\n"); + + } + error = mxt_read_block(mxt->client, page_address, 1, + &page); + if (error < 0) + return error; + mxt_debug(DEBUG_TRACE, "debug data page = %d\n", page); + } + + /* + * Lock mutex to prevent writing some unwanted data to debug + * command register. User can still write through the char + * device interface though. TODO: fix? + */ + + mutex_lock(&mxt->debug_mutex); + /* Configure Debug Diagnostics object to show deltas/refs */ + error = mxt_write_byte(mxt->client, diagnostics_reg, + debug_command); + + /* Wait for command to be handled; when it has, the + * register will be cleared. */ + debug_command_reg = 1; + while (debug_command_reg != 0) { + error = mxt_read_block(mxt->client, + diagnostics_reg, 1, + &debug_command_reg); + if (error < 0) + return error; + mxt_debug(DEBUG_TRACE, "Waiting for debug diag command " + "to propagate...\n"); + + } + + if (error < 0) { + printk(KERN_WARNING + "Error writing to maXTouch device!\n"); + return error; + } + + size = mxt->device_info.num_nodes * sizeof(u16); + + while (size > 0) { + read_size = size > 128 ? 128 : size; + mxt_debug(DEBUG_TRACE, + "Debug data read loop, reading %d bytes...\n", + read_size); + error = mxt_read_block(mxt->client, + debug_data_addr, + read_size, + (u8 *) &data[offset]); + if (error < 0) { + printk(KERN_WARNING + "Error reading debug data\n"); + goto error; + } + offset += read_size / 2; + size -= read_size; + + /* Select next page */ + error = mxt_write_byte(mxt->client, diagnostics_reg, + MXT_CMD_T6_PAGE_UP); + if (error < 0) { + printk(KERN_WARNING + "Error writing to maXTouch device!\n"); + goto error; + } + } + mutex_unlock(&mxt->debug_mutex); + } + + buf_start = buf; + i = mxt->current_debug_datap; + + while (((buf - buf_start) < (count - 6)) && + (i < mxt->device_info.num_nodes)) { + + mxt->current_debug_datap++; + if (debug_command == MXT_CMD_T6_REFERENCES_MODE) + buf += sprintf(buf, "%d: %5d\n", i, + (u16) le16_to_cpu(data[i])); + else if (debug_command == MXT_CMD_T6_DELTAS_MODE) + buf += sprintf(buf, "%d: %5d\n", i, + (s16) le16_to_cpu(data[i])); + i++; + } + + return buf - buf_start; + error: + mutex_unlock(&mxt->debug_mutex); + return error; +} + +ssize_t deltas_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + return debug_data_read(file->private_data, buf, count, ppos, + MXT_CMD_T6_DELTAS_MODE); +} + +ssize_t refs_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + return debug_data_read(file->private_data, buf, count, ppos, + MXT_CMD_T6_REFERENCES_MODE); +} + +int debug_data_open(struct inode *inode, struct file *file) +{ + struct mxt_data *mxt; + int i; + mxt = inode->i_private; + if (mxt == NULL) + return -EIO; + mxt->current_debug_datap = 0; + mxt->debug_data = kmalloc(mxt->device_info.num_nodes * sizeof(u16), + GFP_KERNEL); + if (mxt->debug_data == NULL) + return -ENOMEM; + + for (i = 0; i < mxt->device_info.num_nodes; i++) + mxt->debug_data[i] = 7777; + + file->private_data = mxt; + return 0; +} + +int debug_data_release(struct inode *inode, struct file *file) +{ + struct mxt_data *mxt; + mxt = file->private_data; + kfree(mxt->debug_data); + return 0; +} + +const struct file_operations delta_fops = { + .owner = THIS_MODULE, + .open = debug_data_open, + .release = debug_data_release, + .read = deltas_read, +}; + +const struct file_operations refs_fops = { + .owner = THIS_MODULE, + .open = debug_data_open, + .release = debug_data_release, + .read = refs_read, +}; + +int mxt_memory_open(struct inode *inode, struct file *file) +{ + struct mxt_data *mxt; + mxt = container_of(inode->i_cdev, struct mxt_data, cdev); + if (mxt == NULL) + return -EIO; + file->private_data = mxt; + return 0; +} + +int mxt_message_open(struct inode *inode, struct file *file) +{ + struct mxt_data *mxt; + mxt = container_of(inode->i_cdev, struct mxt_data, cdev_messages); + if (mxt == NULL) + return -EIO; + file->private_data = mxt; + return 0; +} + +ssize_t mxt_memory_read(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + int i; + struct mxt_data *mxt; + + mxt = file->private_data; + if (mxt->valid_ap) { + mxt_debug(DEBUG_TRACE, "Reading %d bytes from current ap\n", + (int)count); + i = mxt_read_block_wo_addr(mxt->client, count, (u8 *) buf); + } else { + mxt_debug(DEBUG_TRACE, "Address pointer changed since set;" + "writing AP (%d) before reading %d bytes", + mxt->address_pointer, (int)count); + i = mxt_read_block(mxt->client, mxt->address_pointer, count, + buf); + } + + return i; +} + +ssize_t mxt_memory_write(struct file *file, const char *buf, size_t count, + loff_t *ppos) +{ + int i; + int whole_blocks; + int last_block_size; + struct mxt_data *mxt; + u16 address; + + mxt = file->private_data; + address = mxt->address_pointer; + + mxt_debug(DEBUG_TRACE, "mxt_memory_write entered\n"); + whole_blocks = count / I2C_PAYLOAD_SIZE; + last_block_size = count % I2C_PAYLOAD_SIZE; + + for (i = 0; i < whole_blocks; i++) { + mxt_debug(DEBUG_TRACE, "About to write to %d...", address); + mxt_write_block(mxt->client, address, I2C_PAYLOAD_SIZE, + (u8 *) buf); + address += I2C_PAYLOAD_SIZE; + buf += I2C_PAYLOAD_SIZE; + } + + mxt_write_block(mxt->client, address, last_block_size, (u8 *) buf); + + return count; +} + +static int mxt_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int retval; + struct mxt_data *mxt; + + retval = 0; + mxt = file->private_data; + + switch (cmd) { + case MXT_SET_ADDRESS: + retval = mxt_write_ap(mxt, (u16) arg); + if (retval >= 0) { + mxt->address_pointer = (u16) arg; + mxt->valid_ap = 1; + } + break; + case MXT_RESET: + retval = mxt_write_byte(mxt->client, + MXT_BASE_ADDR + (MXT_GEN_COMMANDPROCESSOR_T6, + mxt) + MXT_ADR_T6_RESET, 1); + break; + case MXT_CALIBRATE: + retval = mxt_write_byte(mxt->client, + MXT_BASE_ADDR + (MXT_GEN_COMMANDPROCESSOR_T6, + mxt) + MXT_ADR_T6_CALIBRATE, 1); + + break; + case MXT_BACKUP: + retval = mxt_write_byte(mxt->client, + MXT_BASE_ADDR + (MXT_GEN_COMMANDPROCESSOR_T6, + mxt) + MXT_ADR_T6_BACKUPNV, + MXT_CMD_T6_BACKUP); + break; + case MXT_NONTOUCH_MSG: + mxt->nontouch_msg_only = 1; + break; + case MXT_ALL_MSG: + mxt->nontouch_msg_only = 0; + break; + default: + return -EIO; + } + + return retval; +} + +/* + * Copies messages from buffer to user space. + * + * NOTE: if less than (mxt->message_size * 5 + 1) bytes requested, + * this will return 0! + * + */ +ssize_t mxt_message_read(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + int i; + struct mxt_data *mxt; + char *buf_start; + + mxt = file->private_data; + if (mxt == NULL) + return -EIO; + buf_start = buf; + + mutex_lock(&mxt->msg_mutex); + /* Copy messages until buffer empty, or 'count' bytes written */ + while ((mxt->msg_buffer_startp != mxt->msg_buffer_endp) && + ((buf - buf_start) < (count - 5 * mxt->message_size - 1))) { + + for (i = 0; i < mxt->message_size; i++) { + buf += sprintf(buf, "[%2X] ", + *(mxt->messages + mxt->msg_buffer_endp * + mxt->message_size + i)); + } + buf += sprintf(buf, "\n"); + if (mxt->msg_buffer_endp < MXT_MESSAGE_BUFFER_SIZE) + mxt->msg_buffer_endp++; + else + mxt->msg_buffer_endp = 0; + } + mutex_unlock(&mxt->msg_mutex); + return buf - buf_start; +} + +const struct file_operations mxt_message_fops = { + .owner = THIS_MODULE, + .open = mxt_message_open, + .read = mxt_message_read, +}; + +const struct file_operations mxt_memory_fops = { + .owner = THIS_MODULE, + .open = mxt_memory_open, + .read = mxt_memory_read, + .write = mxt_memory_write, + .unlocked_ioctl = mxt_ioctl, +}; + +/* Writes the address pointer (to set up following reads). */ + +int mxt_write_ap(struct mxt_data *mxt, u16 ap) +{ + struct i2c_client *client; + __le16 le_ap = cpu_to_le16(ap); + client = mxt->client; + if (mxt != NULL) + mxt->last_read_addr = -1; + if (i2c_master_send(client, (u8 *) &le_ap, 2) == 2) { + mxt_debug(DEBUG_TRACE, "Address pointer set to %d\n", ap); + return 0; + } else { + mxt_debug(DEBUG_INFO, "Error writing address pointer!\n"); + return -EIO; + } +} + +/* Calculates the 24-bit CRC sum. */ +static u32 CRC_24(u32 crc, u8 byte1, u8 byte2) +{ + static const u32 crcpoly = 0x80001B; + u32 result; + u32 data_word; + + data_word = ((((u16) byte2) << 8u) | byte1); + result = ((crc << 1u) ^ data_word); + if (result & 0x1000000) + result ^= crcpoly; + return result; +} + +/* Returns object address in mXT chip, or zero if object is not found */ +static u16 get_object_address(uint8_t object_type, + uint8_t instance, + struct mxt_object *object_table, int max_objs) +{ + uint8_t object_table_index = 0; + uint8_t address_found = 0; + uint16_t address = 0; + struct mxt_object *obj; + + while ((object_table_index < max_objs) && !address_found) { + obj = &object_table[object_table_index]; + if (obj->type == object_type) { + address_found = 1; + /* Are there enough instances defined in the FW? */ + if (obj->instances >= instance) { + address = obj->chip_addr + + (obj->size + 1) * instance; + } else { + return 0; + } + } + object_table_index++; + } + return address; +} + +/* + * Reads a block of bytes from given address from mXT chip. If we are + * reading from message window, and previous read was from message window, + * there's no need to write the address pointer: the mXT chip will + * automatically set the address pointer back to message window start. + */ + +static int mxt_read_block(struct i2c_client *client, + u16 addr, u16 length, u8 *value) +{ + struct i2c_adapter *adapter = client->adapter; + struct i2c_msg msg[2]; + __le16 le_addr; + struct mxt_data *mxt; + + mxt = i2c_get_clientdata(client); + + if (mxt != NULL) { + if ((mxt->last_read_addr == addr) && + (addr == mxt->msg_proc_addr)) { + if (i2c_master_recv(client, value, length) == length) + return length; + else + return -EIO; + } else { + mxt->last_read_addr = addr; + } + } + + mxt_debug(DEBUG_TRACE, "Writing address pointer & reading %d bytes " + "in on i2c transaction...\n", length); + + le_addr = cpu_to_le16(addr); + msg[0].addr = client->addr; + msg[0].flags = 0x00; + msg[0].len = 2; + msg[0].buf = (u8 *) &le_addr; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = length; + msg[1].buf = (u8 *) value; + if (i2c_transfer(adapter, msg, 2) == 2) + return length; + else + return -EIO; + +} + +/* Reads a block of bytes from current address from mXT chip. */ + +static int mxt_read_block_wo_addr(struct i2c_client *client, + u16 length, u8 *value) +{ + + if (i2c_master_recv(client, value, length) == length) { + mxt_debug(DEBUG_TRACE, "I2C block read ok\n"); + return length; + } else { + mxt_debug(DEBUG_INFO, "I2C block read failed\n"); + return -EIO; + } + +} + +/* Writes one byte to given address in mXT chip. */ + +static int mxt_write_byte(struct i2c_client *client, u16 addr, u8 value) +{ + struct { + __le16 le_addr; + u8 data; + + } i2c_byte_transfer; + + struct mxt_data *mxt; + + mxt = i2c_get_clientdata(client); + if (mxt != NULL) + mxt->last_read_addr = -1; + i2c_byte_transfer.le_addr = cpu_to_le16(addr); + i2c_byte_transfer.data = value; + if (i2c_master_send(client, (u8 *) &i2c_byte_transfer, 3) == 3) + return 0; + else + return -EIO; +} + +/* Writes a block of bytes (max 256) to given address in mXT chip. */ +static int mxt_write_block(struct i2c_client *client, + u16 addr, u16 length, u8 *value) +{ + int i; + struct { + __le16 le_addr; + u8 data[256]; + + } i2c_block_transfer; + + struct mxt_data *mxt; + + mxt_debug(DEBUG_TRACE, "Writing %d bytes to %d...", length, addr); + if (length > 256) + return -EINVAL; + mxt = i2c_get_clientdata(client); + if (mxt != NULL) + mxt->last_read_addr = -1; + for (i = 0; i < length; i++) + i2c_block_transfer.data[i] = *value++; + i2c_block_transfer.le_addr = cpu_to_le16(addr); + i = i2c_master_send(client, (u8 *) &i2c_block_transfer, length + 2); + if (i == (length + 2)) + return length; + else + return -EIO; +} + +/* Calculates the CRC value for mXT infoblock. */ +int calculate_infoblock_crc(u32 *crc_result, u8 *data, int crc_area_size) +{ + u32 crc = 0; + int i; + + for (i = 0; i < (crc_area_size - 1); i = i + 2) + crc = CRC_24(crc, *(data + i), *(data + i + 1)); + /* If uneven size, pad with zero */ + if (crc_area_size & 0x0001) + crc = CRC_24(crc, *(data + i), 0); + /* Return only 24 bits of CRC. */ + *crc_result = (crc & 0x00FFFFFF); + + return 0; +} + +void process_T9_message(u8 *message, struct mxt_data *mxt, int last_touch) +{ + + struct input_dev *input; + u8 status; + u16 xpos = 0xFFFF; + u16 ypos = 0xFFFF; + u8 touch_size = 255; + u8 touch_number; + u8 amplitude; + u8 report_id; + + static int stored_size[10]; + static int stored_x[10]; + static int stored_y[10]; + int i; + int active_touches = 0; + /* + * If the 'last_touch' flag is set, we have received + all the touch messages + * there are available in this cycle, so send the + events for touches that are + * active. + */ + if (last_touch) { + for (i = 0; i < 10; i++) { + if (stored_size[i]) { + active_touches++; + input_report_abs(mxt->input, ABS_MT_TRACKING_ID, + i); + input_report_abs(mxt->input, ABS_MT_TOUCH_MAJOR, + stored_size[i]); + input_report_abs(mxt->input, ABS_MT_POSITION_X, + stored_x[i]); + input_report_abs(mxt->input, ABS_MT_POSITION_Y, + stored_y[i]); + input_mt_sync(mxt->input); + } + } + if (active_touches) + input_sync(mxt->input); + else { + input_mt_sync(mxt->input); + input_sync(mxt->input); + } + + } else { + + input = mxt->input; + status = message[MXT_MSG_T9_STATUS]; + report_id = message[0]; + + if (status & MXT_MSGB_T9_SUPPRESS) { + /* Touch has been suppressed by grip/face */ + /* detection */ + mxt_debug(DEBUG_TRACE, "SUPRESS"); + } else { + xpos = message[MXT_MSG_T9_XPOSMSB] * 16 + + ((message[MXT_MSG_T9_XYPOSLSB] >> 4) & 0xF); + ypos = message[MXT_MSG_T9_YPOSMSB] * 16 + + ((message[MXT_MSG_T9_XYPOSLSB] >> 0) & 0xF); + if (mxt->max_x_val < 1024) + xpos >>= 2; + if (mxt->max_y_val < 1024) + ypos >>= 2; + + touch_number = message[MXT_MSG_REPORTID] - + mxt->rid_map[report_id].first_rid; + + stored_x[touch_number] = xpos; + stored_y[touch_number] = ypos; + + if (status & MXT_MSGB_T9_DETECT) { + /* + * TODO: more precise touch size calculation? + * mXT224 reports the number of touched nodes, + * so the exact value for touch ellipse major + * axis length would be 2*sqrt(touch_size/pi) + * (assuming round touch shape). + */ + touch_size = message[MXT_MSG_T9_TCHAREA]; + touch_size = touch_size >> 2; + if (!touch_size) + touch_size = 1; + + stored_size[touch_number] = touch_size; + + if (status & MXT_MSGB_T9_AMP) + /* Amplitude of touch has changed */ + amplitude = + message[MXT_MSG_T9_TCHAMPLITUDE]; + } + + if (status & MXT_MSGB_T9_RELEASE) { + /* The previously reported touch has + been removed. */ + stored_size[touch_number] = 0; + } + } + + if (status & MXT_MSGB_T9_SUPPRESS) { + mxt_debug(DEBUG_TRACE, "SUPRESS"); + } else { + if (status & MXT_MSGB_T9_DETECT) { + mxt_debug(DEBUG_TRACE, "DETECT:%s%s%s%s", + ((status & MXT_MSGB_T9_PRESS) ? + " PRESS" : ""), + ((status & MXT_MSGB_T9_MOVE) ? " MOVE" + : ""), + ((status & MXT_MSGB_T9_AMP) ? " AMP" : + ""), + ((status & MXT_MSGB_T9_VECTOR) ? + " VECT" : "")); + + } else if (status & MXT_MSGB_T9_RELEASE) { + mxt_debug(DEBUG_TRACE, "RELEASE"); + } + } + mxt_debug(DEBUG_TRACE, "X=%d, Y=%d, TOUCHSIZE=%d", + xpos, ypos, touch_size); + } + return; +} + +int process_message(u8 *message, u8 object, struct mxt_data *mxt) +{ + struct i2c_client *client; + u8 status; + u16 xpos = 0xFFFF; + u16 ypos = 0xFFFF; + u8 event; + u8 length; + u8 report_id; + + client = mxt->client; + length = mxt->message_size; + report_id = message[0]; + + if ((mxt->nontouch_msg_only == 0) || (!IS_TOUCH_OBJECT(object))) { + mutex_lock(&mxt->msg_mutex); + /* Copy the message to buffer */ + if (mxt->msg_buffer_startp < MXT_MESSAGE_BUFFER_SIZE) + mxt->msg_buffer_startp++; + else + mxt->msg_buffer_startp = 0; + + if (mxt->msg_buffer_startp == mxt->msg_buffer_endp) { + mxt_debug(DEBUG_TRACE, + "Message buf full, discarding last entry.\n"); + if (mxt->msg_buffer_endp < MXT_MESSAGE_BUFFER_SIZE) + mxt->msg_buffer_endp++; + else + mxt->msg_buffer_endp = 0; + } + memcpy((mxt->messages + mxt->msg_buffer_startp * length), + message, length); + mutex_unlock(&mxt->msg_mutex); + } + + switch (object) { + case MXT_GEN_COMMANDPROCESSOR_T6: + status = message[1]; + if (status & MXT_MSGB_T6_COMSERR) + dev_err(&client->dev, "maXTouch checksum error\n"); + if (status & MXT_MSGB_T6_CFGERR) { + /* + * Configuration error. A proper configuration + * needs to be written to chip and backed up. Refer + * to protocol document for further info. + */ + dev_err(&client->dev, "maXTouch configuration error\n"); + } + if (status & MXT_MSGB_T6_CAL) { + /* Calibration in action, no need to react */ + dev_info(&client->dev, + "maXTouch calibration in progress\n"); + } + if (status & MXT_MSGB_T6_SIGERR) { + /* + * Signal acquisition error, something is seriously + * wrong, not much we can in the driver to correct + * this + */ + dev_err(&client->dev, "maXTouch acquisition error\n"); + } + if (status & MXT_MSGB_T6_OFL) { + /* + * Cycle overflow, the acquisition is too short. + * Can happen temporarily when there's a complex + * touch shape on the screen requiring lots of + * processing. + */ + dev_err(&client->dev, "maXTouch cycle overflow\n"); + } + if (status & MXT_MSGB_T6_RESET) { + /* Chip has reseted, no need to react. */ + dev_info(&client->dev, "maXTouch chip reset\n"); + } + if (status == 0) { + /* Chip status back to normal. */ + dev_info(&client->dev, "maXTouch status normal\n"); + } + break; + + case MXT_TOUCH_MULTITOUCHSCREEN_T9: + process_T9_message(message, mxt, 0); + break; + + case MXT_SPT_GPIOPWM_T19: + if (debug >= DEBUG_TRACE) + dev_info(&client->dev, "Receiving GPIO message\n"); + break; + + case MXT_PROCI_GRIPFACESUPPRESSION_T20: + if (debug >= DEBUG_TRACE) + dev_info(&client->dev, + "Receiving face suppression msg\n"); + break; + + case MXT_PROCG_NOISESUPPRESSION_T22: + if (debug >= DEBUG_TRACE) + dev_info(&client->dev, + "Receiving noise suppression msg\n"); + status = message[MXT_MSG_T22_STATUS]; + if (status & MXT_MSGB_T22_FHCHG) { + if (debug >= DEBUG_TRACE) + dev_info(&client->dev, + "maXTouch: Freq changed\n"); + } + if (status & MXT_MSGB_T22_GCAFERR) { + if (debug >= DEBUG_TRACE) + dev_info(&client->dev, + "maXTouch: High noise " "level\n"); + } + if (status & MXT_MSGB_T22_FHERR) { + if (debug >= DEBUG_TRACE) + dev_info(&client->dev, + "maXTouch: Freq changed - " + "Noise level too high\n"); + } + break; + + case MXT_PROCI_ONETOUCHGESTUREPROCESSOR_T24: + if (debug >= DEBUG_TRACE) + dev_info(&client->dev, + "Receiving one-touch gesture msg\n"); + + event = message[MXT_MSG_T24_STATUS] & 0x0F; + xpos = message[MXT_MSG_T24_XPOSMSB] * 16 + + ((message[MXT_MSG_T24_XYPOSLSB] >> 4) & 0x0F); + ypos = message[MXT_MSG_T24_YPOSMSB] * 16 + + ((message[MXT_MSG_T24_XYPOSLSB] >> 0) & 0x0F); + xpos >>= 2; + ypos >>= 2; + + switch (event) { + case MT_GESTURE_RESERVED: + break; + case MT_GESTURE_PRESS: + break; + case MT_GESTURE_RELEASE: + break; + case MT_GESTURE_TAP: + break; + case MT_GESTURE_DOUBLE_TAP: + break; + case MT_GESTURE_FLICK: + break; + case MT_GESTURE_DRAG: + break; + case MT_GESTURE_SHORT_PRESS: + break; + case MT_GESTURE_LONG_PRESS: + break; + case MT_GESTURE_REPEAT_PRESS: + break; + case MT_GESTURE_TAP_AND_PRESS: + break; + case MT_GESTURE_THROW: + break; + default: + break; + } + break; + + case MXT_SPT_SELFTEST_T25: + if (debug >= DEBUG_TRACE) + dev_info(&client->dev, "Receiving Self-Test msg\n"); + + if (message[MXT_MSG_T25_STATUS] == MXT_MSGR_T25_OK) { + if (debug >= DEBUG_TRACE) + dev_info(&client->dev, + "maXTouch: Self-Test OK\n"); + + } else { + dev_err(&client->dev, + "maXTouch: Self-Test Failed [%02x]:" + "{%02x,%02x,%02x,%02x,%02x}\n", + message[MXT_MSG_T25_STATUS], + message[MXT_MSG_T25_STATUS + 0], + message[MXT_MSG_T25_STATUS + 1], + message[MXT_MSG_T25_STATUS + 2], + message[MXT_MSG_T25_STATUS + 3], + message[MXT_MSG_T25_STATUS + 4] + ); + } + break; + + case MXT_PROCI_TWOTOUCHGESTUREPROCESSOR_T27: + if (debug >= DEBUG_TRACE) + dev_info(&client->dev, + "Receiving 2-touch gesture message\n"); + break; + + case MXT_SPT_CTECONFIG_T28: + if (debug >= DEBUG_TRACE) + dev_info(&client->dev, "Receiving CTE message...\n"); + status = message[MXT_MSG_T28_STATUS]; + if (status & MXT_MSGB_T28_CHKERR) + dev_err(&client->dev, + "maXTouch: Power-Up CRC failure\n"); + + break; + default: + if (debug >= DEBUG_TRACE) + dev_info(&client->dev, "maXTouch: Unknown message!\n"); + + break; + } + + return 0; +} + +/* + * Processes messages when the interrupt line (CHG) is asserted. Keeps + * reading messages until a message with report ID 0xFF is received, + * which indicates that there is no more new messages. + * + */ + +static void mxt_worker(struct work_struct *work) +{ + struct mxt_data *mxt; + struct i2c_client *client; + + u8 *message; + u16 message_length; + u16 message_addr; + u8 report_id; + u8 object; + int error; + int i; + char *message_string; + char *message_start; + + int n = 0; + + message = NULL; + mxt = container_of(work, struct mxt_data, dwork.work); + disable_irq(mxt->irq); + client = mxt->client; + message_addr = mxt->msg_proc_addr; + message_length = mxt->message_size; + + if (message_length < 256) { + message = kmalloc(message_length, GFP_KERNEL); + if (message == NULL) { + dev_err(&client->dev, "Error allocating memory\n"); + return; + } + } else { + dev_err(&client->dev, + "Message length larger than 256 bytes not supported\n"); + return; + } + + mxt_debug("maXTouch worker active: \n"); + do { + /* Read next message, reread on failure. */ + /* -1 TO WORK AROUND A BUG ON 0.9 FW MESSAGING, needs */ + /* to be changed back if checksum is read */ + mxt->message_counter++; + for (i = 1; i < I2C_RETRY_COUNT; i++) { + error = mxt_read_block(client, + message_addr, + message_length - 1, message); + if (error >= 0) + break; + mxt->read_fail_counter++; + dev_err(&client->dev, + "Failure reading maxTouch device\n"); + } + if (error < 0) { + kfree(message); + return; + } + + if (mxt->address_pointer != message_addr) + mxt->valid_ap = 0; + report_id = message[0]; + + if (debug >= DEBUG_RAW) { + mxt_debug(DEBUG_RAW, "%s message [msg count: %08x]:", + REPORT_ID_TO_OBJECT_NAME(report_id, mxt), + mxt->message_counter); + /* 5 characters per one byte */ + message_string = kmalloc(message_length * 5, + GFP_KERNEL); + if (message_string == NULL) { + dev_err(&client->dev, + "Error allocating memory\n"); + kfree(message); + return; + } + message_start = message_string; + for (i = 0; i < message_length; i++) { + message_string += + sprintf(message_string, + "0x%02X ", message[i]); + } + mxt_debug(DEBUG_RAW, "%s", message_start); + kfree(message_start); + } + + if ((report_id != MXT_END_OF_MESSAGES) && (report_id != 0)) { + memcpy(mxt->last_message, message, message_length); + mxt->new_msgs = 1; + smp_wmb(); + /* Get type of object and process the message */ + object = mxt->rid_map[report_id].object; + process_message(message, object, mxt); + } + mxt_debug(DEBUG_TRACE, "chgline: %d\n", mxt->read_chg()); + } while (comms ? (mxt->read_chg() == 0) : + ((report_id != MXT_END_OF_MESSAGES) && (report_id != 0))); + + /* All messages processed, send the events) */ + process_T9_message(NULL, mxt, 1); + + kfree(message); + enable_irq(mxt->irq); + /* Make sure we don't miss any interrupts and read changeline. */ + if (mxt->read_chg() == 0) + schedule_delayed_work(&mxt->dwork, 0); +} + +/* + * The maXTouch device will signal the host about a new message by asserting + * the CHG line. This ISR schedules a worker routine to read the message when + * that happens. + */ + +static irqreturn_t mxt_irq_handler(int irq, void *_mxt) +{ + struct mxt_data *mxt = _mxt; + + mxt->irq_counter++; + if (mxt->valid_interrupt()) { + /* Send the signal only if falling edge generated the irq. */ + cancel_delayed_work(&mxt->dwork); + schedule_delayed_work(&mxt->dwork, 0); + mxt->valid_irq_counter++; + } else { + mxt->invalid_irq_counter++; + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +/******************************************************************************/ +/* Initialization of driver */ +/******************************************************************************/ + +static int __devinit mxt_identify(struct i2c_client *client, + struct mxt_data *mxt, u8 * id_block_data) +{ + u8 buf[7]; + int error; + int identified; + + identified = 0; + + /* Read Device info to check if chip is valid */ + error = mxt_read_block(client, MXT_ADDR_INFO_BLOCK, MXT_ID_BLOCK_SIZE, + (u8 *) buf); + + if (error < 0) { + mxt->read_fail_counter++; + dev_err(&client->dev, "Failure accessing maXTouch device\n"); + return -EIO; + } + + memcpy(id_block_data, buf, MXT_ID_BLOCK_SIZE); + + mxt->device_info.family_id = buf[0]; + mxt->device_info.variant_id = buf[1]; + mxt->device_info.major = ((buf[2] >> 4) & 0x0F); + mxt->device_info.minor = (buf[2] & 0x0F); + mxt->device_info.build = buf[3]; + mxt->device_info.x_size = buf[4]; + mxt->device_info.y_size = buf[5]; + mxt->device_info.num_objs = buf[6]; + mxt->device_info.num_nodes = mxt->device_info.x_size * + mxt->device_info.y_size; + + /* + * Check Family & Variant Info; warn if not recognized but + * still continue. + */ + + /* MXT224 */ + if (mxt->device_info.family_id == MXT224_FAMILYID) { + strcpy(mxt->device_info.family_name, "mXT224"); + + if (mxt->device_info.variant_id == MXT224_CAL_VARIANTID) { + strcpy(mxt->device_info.variant_name, "Calibrated"); + } else if (mxt->device_info.variant_id == + MXT224_UNCAL_VARIANTID) { + strcpy(mxt->device_info.variant_name, "Uncalibrated"); + } else { + dev_err(&client->dev, + "Warning: maXTouch Variant ID [%d] not " + "supported\n", mxt->device_info.variant_id); + strcpy(mxt->device_info.variant_name, "UNKNOWN"); + /* identified = -ENXIO; */ + } + + /* MXT1386 */ + } else if (mxt->device_info.family_id == MXT1386_FAMILYID) { + strcpy(mxt->device_info.family_name, "mXT1386"); + + if (mxt->device_info.variant_id == MXT1386_CAL_VARIANTID) { + strcpy(mxt->device_info.variant_name, "Calibrated"); + } else { + dev_err(&client->dev, + "Warning: maXTouch Variant ID [%d] not " + "supported\n", mxt->device_info.variant_id); + strcpy(mxt->device_info.variant_name, "UNKNOWN"); + /* identified = -ENXIO; */ + } + /* Unknown family ID! */ + } else { + dev_err(&client->dev, + "Warning: maXTouch Family ID [%d] not supported\n", + mxt->device_info.family_id); + strcpy(mxt->device_info.family_name, "UNKNOWN"); + strcpy(mxt->device_info.variant_name, "UNKNOWN"); + /* identified = -ENXIO; */ + } + + dev_info(&client->dev, + "Atmel maXTouch (Family %s (%X), Variant %s (%X)) Firmware " + "version [%d.%d] Build %d\n", + mxt->device_info.family_name, + mxt->device_info.family_id, + mxt->device_info.variant_name, + mxt->device_info.variant_id, + mxt->device_info.major, + mxt->device_info.minor, mxt->device_info.build); + dev_info(&client->dev, + "Atmel maXTouch Configuration " + "[X: %d] x [Y: %d]\n", + mxt->device_info.x_size, mxt->device_info.y_size); + return identified; +} + +/* + * Reads the object table from maXTouch chip to get object data like + * address, size, report id. For Info Block CRC calculation, already read + * id data is passed to this function too (Info Block consists of the ID + * block and object table). + * + */ +static int __devinit mxt_read_object_table(struct i2c_client *client, + struct mxt_data *mxt, + u8 *raw_id_data) +{ + u16 report_id_count; + u8 buf[MXT_OBJECT_TABLE_ELEMENT_SIZE]; + u8 *raw_ib_data; + u8 object_type; + u16 object_address; + u16 object_size; + u8 object_instances; + u8 object_report_ids; + u16 object_info_address; + u32 crc; + u32 calculated_crc; + int i; + int error; + + u8 object_instance; + u8 object_report_id; + u8 report_id; + int first_report_id; + int ib_pointer; + struct mxt_object *object_table; + + mxt_debug(DEBUG_TRACE, "maXTouch driver reading configuration\n"); + + object_table = kzalloc(sizeof(struct mxt_object) * + mxt->device_info.num_objs, GFP_KERNEL); + if (object_table == NULL) { + printk(KERN_WARNING "maXTouch: Memory allocation failed!\n"); + error = -ENOMEM; + goto err_object_table_alloc; + } + + raw_ib_data = kmalloc(MXT_OBJECT_TABLE_ELEMENT_SIZE * + mxt->device_info.num_objs + MXT_ID_BLOCK_SIZE, + GFP_KERNEL); + if (raw_ib_data == NULL) { + printk(KERN_WARNING "maXTouch: Memory allocation failed!\n"); + error = -ENOMEM; + goto err_ib_alloc; + } + + /* Copy the ID data for CRC calculation. */ + memcpy(raw_ib_data, raw_id_data, MXT_ID_BLOCK_SIZE); + ib_pointer = MXT_ID_BLOCK_SIZE; + + mxt->object_table = object_table; + + mxt_debug(DEBUG_TRACE, "maXTouch driver Memory allocated\n"); + + object_info_address = MXT_ADDR_OBJECT_TABLE; + + report_id_count = 0; + for (i = 0; i < mxt->device_info.num_objs; i++) { + mxt_debug(DEBUG_TRACE, "Reading maXTouch at [0x%04x]: ", + object_info_address); + + error = mxt_read_block(client, object_info_address, + MXT_OBJECT_TABLE_ELEMENT_SIZE, buf); + + if (error < 0) { + mxt->read_fail_counter++; + dev_err(&client->dev, + "maXTouch Object %d could not be read\n", i); + error = -EIO; + goto err_object_read; + } + + memcpy(raw_ib_data + ib_pointer, buf, + MXT_OBJECT_TABLE_ELEMENT_SIZE); + ib_pointer += MXT_OBJECT_TABLE_ELEMENT_SIZE; + + object_type = buf[0]; + object_address = (buf[2] << 8) + buf[1]; + object_size = buf[3] + 1; + object_instances = buf[4] + 1; + object_report_ids = buf[5]; + mxt_debug(DEBUG_TRACE, "Type=%03d, Address=0x%04x, " + "Size=0x%02x, %d instances, %d report id's\n", + object_type, + object_address, + object_size, object_instances, object_report_ids); + + /* TODO: check whether object is known and supported? */ + + /* Save frequently needed info. */ + if (object_type == MXT_GEN_MESSAGEPROCESSOR_T5) { + mxt->msg_proc_addr = object_address; + mxt->message_size = object_size; + printk(KERN_ALERT "message length: %d", object_size); + } + + object_table[i].type = object_type; + object_table[i].chip_addr = object_address; + object_table[i].size = object_size; + object_table[i].instances = object_instances; + object_table[i].num_report_ids = object_report_ids; + report_id_count += object_instances * object_report_ids; + + object_info_address += MXT_OBJECT_TABLE_ELEMENT_SIZE; + } + + mxt->rid_map = + kzalloc(sizeof(struct report_id_map) * (report_id_count + 1), + /* allocate for report_id 0, even if not used */ + GFP_KERNEL); + if (mxt->rid_map == NULL) { + printk(KERN_WARNING "maXTouch: Can't allocate memory!\n"); + error = -ENOMEM; + goto err_rid_map_alloc; + } + + mxt->messages = kzalloc(mxt->message_size * MXT_MESSAGE_BUFFER_SIZE, + GFP_KERNEL); + if (mxt->messages == NULL) { + printk(KERN_WARNING "maXTouch: Can't allocate memory!\n"); + error = -ENOMEM; + goto err_msg_alloc; + } + + mxt->last_message = kzalloc(mxt->message_size, GFP_KERNEL); + if (mxt->last_message == NULL) { + printk(KERN_WARNING "maXTouch: Can't allocate memory!\n"); + error = -ENOMEM; + goto err_msg_alloc; + } + + mxt->report_id_count = report_id_count; + if (report_id_count > 254) { /* 0 & 255 are reserved */ + dev_err(&client->dev, + "Too many maXTouch report id's [%d]\n", + report_id_count); + error = -ENXIO; + goto err_max_rid; + } + + /* Create a mapping from report id to object type */ + report_id = 1; /* Start from 1, 0 is reserved. */ + + /* Create table associating report id's with objects & instances */ + for (i = 0; i < mxt->device_info.num_objs; i++) { + for (object_instance = 0; + object_instance < object_table[i].instances; + object_instance++) { + first_report_id = report_id; + for (object_report_id = 0; + object_report_id < object_table[i].num_report_ids; + object_report_id++) { + mxt->rid_map[report_id].object = + object_table[i].type; + mxt->rid_map[report_id].instance = + object_instance; + mxt->rid_map[report_id].first_rid = + first_report_id; + report_id++; + } + } + } + + /* Read 3 byte CRC */ + error = mxt_read_block(client, object_info_address, 3, buf); + if (error < 0) { + mxt->read_fail_counter++; + dev_err(&client->dev, "Error reading CRC\n"); + } + + crc = (buf[2] << 16) | (buf[1] << 8) | buf[0]; + + if (calculate_infoblock_crc(&calculated_crc, raw_ib_data, ib_pointer)) { + printk(KERN_WARNING "Error while calculating CRC!\n"); + calculated_crc = 0; + } + kfree(raw_ib_data); + + mxt_debug(DEBUG_TRACE, "\nReported info block CRC = 0x%6X\n", crc); + mxt_debug(DEBUG_TRACE, "Calculated info block CRC = 0x%6X\n\n", + calculated_crc); + + if (crc == calculated_crc) { + mxt->info_block_crc = crc; + } else { + mxt->info_block_crc = 0; + printk(KERN_ALERT "maXTouch: Info block CRC invalid!\n"); + } + + if (debug >= DEBUG_VERBOSE) { + + dev_info(&client->dev, "maXTouch: %d Objects\n", + mxt->device_info.num_objs); + + for (i = 0; i < mxt->device_info.num_objs; i++) { + dev_info(&client->dev, "Type:\t\t\t[%d]: %s\n", + object_table[i].type, + object_type_name[object_table[i].type]); + dev_info(&client->dev, "\tAddress:\t0x%04X\n", + object_table[i].chip_addr); + dev_info(&client->dev, "\tSize:\t\t%d Bytes\n", + object_table[i].size); + dev_info(&client->dev, "\tInstances:\t%d\n", + object_table[i].instances); + dev_info(&client->dev, "\tReport Id's:\t%d\n", + object_table[i].num_report_ids); + } + } + + return 0; + + err_max_rid: + kfree(mxt->last_message); + err_msg_alloc: + kfree(mxt->rid_map); + err_rid_map_alloc: + err_object_read: + kfree(raw_ib_data); + err_ib_alloc: + kfree(object_table); + err_object_table_alloc: + return error; +} + +static int __devinit mxt_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mxt_data *mxt; + struct mxt_platform_data *pdata; + struct input_dev *input; + u8 *id_data; + int error; + + mxt_debug(DEBUG_INFO, "mXT224: mxt_probe\n"); + + if (client == NULL) { + pr_debug("maXTouch: client == NULL\n"); + return -EINVAL; + } else if (client->adapter == NULL) { + pr_debug("maXTouch: client->adapter == NULL\n"); + return -EINVAL; + } else if (&client->dev == NULL) { + pr_debug("maXTouch: client->dev == NULL\n"); + return -EINVAL; + } else if (&client->adapter->dev == NULL) { + pr_debug("maXTouch: client->adapter->dev == NULL\n"); + return -EINVAL; + } else if (id == NULL) { + pr_debug("maXTouch: id == NULL\n"); + return -EINVAL; + } + + mxt_debug(DEBUG_INFO, "maXTouch driver\n"); + mxt_debug(DEBUG_INFO, "\t \"%s\"\n", client->name); + mxt_debug(DEBUG_INFO, "\taddr:\t0x%04x\n", client->addr); + mxt_debug(DEBUG_INFO, "\tirq:\t%d\n", client->irq); + mxt_debug(DEBUG_INFO, "\tflags:\t0x%04x\n", client->flags); + mxt_debug(DEBUG_INFO, "\tadapter:\"%s\"\n", client->adapter->name); + mxt_debug(DEBUG_INFO, "\tdevice:\t\"%s\"\n", client->dev.init_name); + + /* Check if the I2C bus supports BYTE transfer */ + error = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE); + dev_info(&client->dev, "RRC: i2c_check_functionality = %i\n", error); + error = 0xff; +/* + if (!error) { + dev_err(&client->dev, "maXTouch driver\n"); + dev_err(&client->dev, "\t \"%s\"\n", client->name); + dev_err(&client->dev, "\taddr:\t0x%04x\n", client->addr); + dev_err(&client->dev, "\tirq:\t%d\n", client->irq); + dev_err(&client->dev, "\tflags:\t0x%04x\n", client->flags); + dev_err(&client->dev, "\tadapter:\"%s\"\n", + client->adapter->name); + dev_err(&client->dev, "\tdevice:\t\"%s\"\n", + client->dev.init_name); + dev_err(&client->dev, "%s adapter not supported\n", + dev_driver_string(&client->adapter->dev)); + return -ENODEV; + } +*/ + mxt_debug(DEBUG_TRACE, "maXTouch driver functionality OK\n"); + + /* Allocate structure - we need it to identify device */ + mxt = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); + if (mxt == NULL) { + dev_err(&client->dev, "insufficient memory\n"); + error = -ENOMEM; + goto err_mxt_alloc; + } + + id_data = kmalloc(MXT_ID_BLOCK_SIZE, GFP_KERNEL); + if (id_data == NULL) { + dev_err(&client->dev, "insufficient memory\n"); + error = -ENOMEM; + goto err_id_alloc; + } + + input = input_allocate_device(); + if (!input) { + dev_err(&client->dev, "error allocating input device\n"); + error = -ENOMEM; + goto err_input_dev_alloc; + } + + /* Initialize Platform data */ + + pdata = client->dev.platform_data; + if (pdata == NULL) { + dev_err(&client->dev, "platform data is required!\n"); + error = -EINVAL; + goto err_pdata; + } + if (debug >= DEBUG_TRACE) + printk(KERN_INFO "Platform OK: pdata = 0x%08x\n", + (unsigned int)pdata); + + mxt->read_fail_counter = 0; + mxt->message_counter = 0; + mxt->max_x_val = pdata->max_x; + mxt->max_y_val = pdata->max_y; + + /* Get data that is defined in board specific code. */ + mxt->init_hw = pdata->init_platform_hw; + mxt->exit_hw = pdata->exit_platform_hw; + mxt->read_chg = pdata->read_chg; + + if (pdata->valid_interrupt != NULL) + mxt->valid_interrupt = pdata->valid_interrupt; + else + mxt->valid_interrupt = mxt_valid_interrupt_dummy; + + if (mxt->init_hw != NULL) + mxt->init_hw(); + + if (debug >= DEBUG_TRACE) + printk(KERN_INFO "maXTouch driver identifying chip\n"); + + if (mxt_identify(client, mxt, id_data) < 0) { + dev_err(&client->dev, "Chip could not be identified\n"); + error = -ENODEV; + goto err_identify; + } + /* Chip is valid and active. */ + if (debug >= DEBUG_TRACE) + printk(KERN_INFO "maXTouch driver allocating input device\n"); + + mxt->client = client; + mxt->input = input; + + INIT_DELAYED_WORK(&mxt->dwork, mxt_worker); + mutex_init(&mxt->debug_mutex); + mutex_init(&mxt->msg_mutex); + mxt_debug(DEBUG_TRACE, "maXTouch driver creating device name\n"); + + snprintf(mxt->phys_name, + sizeof(mxt->phys_name), "%s/input0", dev_name(&client->dev) + ); + input->name = "atmel-maxtouch"; + input->phys = mxt->phys_name; + input->id.bustype = BUS_I2C; + input->dev.parent = &client->dev; + + mxt_debug(DEBUG_INFO, "maXTouch name: \"%s\"\n", input->name); + mxt_debug(DEBUG_INFO, "maXTouch phys: \"%s\"\n", input->phys); + mxt_debug(DEBUG_INFO, "maXTouch driver setting abs parameters\n"); + + set_bit(BTN_TOUCH, input->keybit); + + /* Single touch */ + input_set_abs_params(input, ABS_X, 0, mxt->max_x_val, 0, 0); + input_set_abs_params(input, ABS_Y, 0, mxt->max_y_val, 0, 0); + input_set_abs_params(input, ABS_PRESSURE, 0, MXT_MAX_REPORTED_PRESSURE, + 0, 0); + input_set_abs_params(input, ABS_TOOL_WIDTH, 0, MXT_MAX_REPORTED_WIDTH, + 0, 0); + + /* Multitouch */ + input_set_abs_params(input, ABS_MT_POSITION_X, 0, mxt->max_x_val, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, mxt->max_y_val, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, MXT_MAX_TOUCH_SIZE, + 0, 0); + input_set_abs_params(input, ABS_MT_TRACKING_ID, 0, MXT_MAX_NUM_TOUCHES, + 0, 0); + + __set_bit(EV_ABS, input->evbit); + __set_bit(EV_SYN, input->evbit); + __set_bit(EV_KEY, input->evbit); + + mxt_debug(DEBUG_TRACE, "maXTouch driver setting client data\n"); + i2c_set_clientdata(client, mxt); + mxt_debug(DEBUG_TRACE, "maXTouch driver setting drv data\n"); + input_set_drvdata(input, mxt); + mxt_debug(DEBUG_TRACE, "maXTouch driver input register device\n"); + error = input_register_device(mxt->input); + if (error < 0) { + dev_err(&client->dev, "Failed to register input device\n"); + goto err_register_device; + } + + error = mxt_read_object_table(client, mxt, id_data); + if (error < 0) + goto err_read_ot; + + /* Create debugfs entries. */ + mxt->debug_dir = debugfs_create_dir("maXTouch", NULL); + if (mxt->debug_dir == -ENODEV) { + /* debugfs is not enabled. */ + printk(KERN_WARNING "debugfs not enabled in kernel\n"); + } else if (mxt->debug_dir == NULL) { + printk(KERN_WARNING "error creating debugfs dir\n"); + } else { + mxt_debug(DEBUG_TRACE, "created \"maXTouch\" debugfs dir\n"); + + debugfs_create_file("deltas", S_IRUSR, mxt->debug_dir, mxt, + &delta_fops); + debugfs_create_file("refs", S_IRUSR, mxt->debug_dir, mxt, + &refs_fops); + } + + /* Create character device nodes for reading & writing registers */ + mxt->mxt_class = class_create(THIS_MODULE, "maXTouch_memory"); + /* 2 numbers; one for memory and one for messages */ + error = alloc_chrdev_region(&mxt->dev_num, 0, 2, "maXTouch_memory"); + mxt_debug(DEBUG_VERBOSE, + "device number %d allocated!\n", MAJOR(mxt->dev_num)); + if (error) + printk(KERN_WARNING "Error registering device\n"); + cdev_init(&mxt->cdev, &mxt_memory_fops); + cdev_init(&mxt->cdev_messages, &mxt_message_fops); + + mxt_debug(DEBUG_VERBOSE, "cdev initialized\n"); + mxt->cdev.owner = THIS_MODULE; + mxt->cdev_messages.owner = THIS_MODULE; + + error = cdev_add(&mxt->cdev, mxt->dev_num, 1); + if (error) + printk(KERN_WARNING "Bad cdev\n"); + + error = cdev_add(&mxt->cdev_messages, mxt->dev_num + 1, 1); + if (error) + printk(KERN_WARNING "Bad cdev\n"); + + mxt_debug(DEBUG_VERBOSE, "cdev added\n"); + + device_create(mxt->mxt_class, NULL, MKDEV(MAJOR(mxt->dev_num), 0), NULL, + "maXTouch"); + + device_create(mxt->mxt_class, NULL, MKDEV(MAJOR(mxt->dev_num), 1), NULL, + "maXTouch_messages"); + + mxt->msg_buffer_startp = 0; + mxt->msg_buffer_endp = 0; + + /* Allocate the interrupt */ + mxt_debug(DEBUG_TRACE, "maXTouch driver allocating interrupt...\n"); + mxt->irq = client->irq; + mxt->valid_irq_counter = 0; + mxt->invalid_irq_counter = 0; + mxt->irq_counter = 0; + if (mxt->irq) { + /* Try to request IRQ with falling edge first. This is + * not always supported. If it fails, try with any edge. */ + error = request_irq(mxt->irq, + mxt_irq_handler, + IRQF_TRIGGER_FALLING, + client->dev.driver->name, mxt); + if (error < 0) { + /* TODO: why only 0 works on STK1000? */ + error = request_irq(mxt->irq, + mxt_irq_handler, + 0, client->dev.driver->name, mxt); + } + + if (error < 0) { + dev_err(&client->dev, + "failed to allocate irq %d\n", mxt->irq); + goto err_irq; + } + } + + if (debug > DEBUG_INFO) + dev_info(&client->dev, "touchscreen, irq %d\n", mxt->irq); + + /* Schedule a worker routine to read any messages that might have + * been sent before interrupts were enabled. */ + cancel_delayed_work(&mxt->dwork); + schedule_delayed_work(&mxt->dwork, 0); + kfree(id_data); + + /* + TODO: REMOVE!!!!!!!!!!!!!!!!!!!!!!! + + REMOVE!!!!!!!!!!!!!!!!!!!!!!! + */ + mxt_write_byte(mxt->client, + MXT_BASE_ADDR(MXT_TOUCH_MULTITOUCHSCREEN_T9, mxt), 15); + + return 0; + + err_irq: + kfree(mxt->rid_map); + kfree(mxt->object_table); + kfree(mxt->last_message); + err_read_ot: + err_register_device: + err_identify: + err_pdata: + input_free_device(input); + err_input_dev_alloc: + kfree(id_data); + err_id_alloc: + if (mxt->exit_hw != NULL) + mxt->exit_hw(); + kfree(mxt); + err_mxt_alloc: + return error; +} + +static int __devexit mxt_remove(struct i2c_client *client) +{ + struct mxt_data *mxt; + + mxt = i2c_get_clientdata(client); + + /* Remove debug dir entries */ + debugfs_remove_recursive(mxt->debug_dir); + + if (mxt != NULL) { + + if (mxt->exit_hw != NULL) + mxt->exit_hw(); + + if (mxt->irq) + free_irq(mxt->irq, mxt); + + unregister_chrdev_region(mxt->dev_num, 2); + device_destroy(mxt->mxt_class, MKDEV(MAJOR(mxt->dev_num), 0)); + device_destroy(mxt->mxt_class, MKDEV(MAJOR(mxt->dev_num), 1)); + cdev_del(&mxt->cdev); + cdev_del(&mxt->cdev_messages); + cancel_delayed_work_sync(&mxt->dwork); + input_unregister_device(mxt->input); + class_destroy(mxt->mxt_class); + debugfs_remove(mxt->debug_dir); + + kfree(mxt->rid_map); + kfree(mxt->object_table); + kfree(mxt->last_message); + } + kfree(mxt); + + i2c_set_clientdata(client, NULL); + if (debug >= DEBUG_TRACE) + dev_info(&client->dev, "Touchscreen unregistered\n"); + + return 0; +} + +#if defined(CONFIG_PM) +static int mxt_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct mxt_data *mxt = i2c_get_clientdata(client); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(mxt->irq); + + return 0; +} + +static int mxt_resume(struct i2c_client *client) +{ + struct mxt_data *mxt = i2c_get_clientdata(client); + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(mxt->irq); + + return 0; +} +#else +#define mxt_suspend NULL +#define mxt_resume NULL +#endif + +static const struct i2c_device_id mxt_idtable[] = { + {"maXTouch", 0,}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, mxt_idtable); + +static struct i2c_driver mxt_driver = { + .driver = { + .name = "maXTouch", + .owner = THIS_MODULE, + }, + + .id_table = mxt_idtable, + .probe = mxt_probe, + .remove = __devexit_p(mxt_remove), + .suspend = mxt_suspend, + .resume = mxt_resume, + +}; + +static int __init mxt_init(void) +{ + int err; + err = i2c_add_driver(&mxt_driver); + if (err) { + printk(KERN_WARNING "Adding maXTouch driver failed " + "(errno = %d)\n", err); + } else { + mxt_debug(DEBUG_TRACE, "Successfully added driver %s\n", + mxt_driver.driver.name); + } + return err; +} + +static void __exit mxt_cleanup(void) +{ + i2c_del_driver(&mxt_driver); +} + +module_init(mxt_init); +module_exit(mxt_cleanup); + +MODULE_AUTHOR("Iiro Valkonen"); +MODULE_DESCRIPTION("Driver for Atmel maXTouch Touchscreen Controller"); +MODULE_LICENSE("GPL"); |