From 0bf25a45386f284d591530ef174eaa9e44d84956 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 3 Apr 2012 13:39:44 -0700 Subject: Input: add support for LM8333 keypads This driver adds support for the keypad part of the LM8333 and is prepared for possible GPIO/PWM drivers. Note that this is not a MFD because you cannot disable the keypad functionality which, thus, has to be handled by the core anyhow. Signed-off-by: Wolfram Sang Signed-off-by: Dmitry Torokhov --- include/linux/input/lm8333.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 include/linux/input/lm8333.h (limited to 'include/linux/input') diff --git a/include/linux/input/lm8333.h b/include/linux/input/lm8333.h new file mode 100644 index 000000000000..79f918c6e8c5 --- /dev/null +++ b/include/linux/input/lm8333.h @@ -0,0 +1,24 @@ +/* + * public include for LM8333 keypad driver - same license as driver + * Copyright (C) 2012 Wolfram Sang, Pengutronix + */ + +#ifndef _LM8333_H +#define _LM8333_H + +struct lm8333; + +struct lm8333_platform_data { + /* Keymap data */ + const struct matrix_keymap_data *matrix_data; + /* Active timeout before enter HALT mode in microseconds */ + unsigned active_time; + /* Debounce interval in microseconds */ + unsigned debounce_time; +}; + +extern int lm8333_read8(struct lm8333 *lm8333, u8 cmd); +extern int lm8333_write8(struct lm8333 *lm8333, u8 cmd, u8 val); +extern int lm8333_read_block(struct lm8333 *lm8333, u8 cmd, u8 len, u8 *buf); + +#endif /* _LM8333_H */ -- cgit v1.2.3 From ae99ea562be0a788ccb583aff21f3d2147de531f Mon Sep 17 00:00:00 2001 From: Paul Parsons Date: Thu, 10 May 2012 22:11:51 -0700 Subject: Input: Add Synaptics NavPoint (PXA27x SSP/SPI) driver This driver adds support for the Synaptics NavPoint touchpad connected to a PXA27x SSP port in SPI slave mode. The device emulates a mouse; a tap or tap-and-a-half drag gesture emulates the left mouse button. For example, use the xf86-input-evdev driver for an X pointing device. Signed-off-by: Paul Parsons Tested-by: Philipp Zabel Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/Kconfig | 12 ++ drivers/input/mouse/Makefile | 1 + drivers/input/mouse/navpoint.c | 369 +++++++++++++++++++++++++++++++++++++++++ include/linux/input/navpoint.h | 12 ++ 4 files changed, 394 insertions(+) create mode 100644 drivers/input/mouse/navpoint.c create mode 100644 include/linux/input/navpoint.h (limited to 'include/linux/input') diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 9b8db821d5f0..cd6268cf7cd5 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -339,4 +339,16 @@ config MOUSE_SYNAPTICS_USB To compile this driver as a module, choose M here: the module will be called synaptics_usb. +config MOUSE_NAVPOINT_PXA27x + tristate "Synaptics NavPoint (PXA27x SSP/SPI)" + depends on PXA27x && PXA_SSP + help + This driver adds support for the Synaptics NavPoint touchpad connected + to a PXA27x SSP port in SPI slave mode. The device emulates a mouse; + a tap or tap-and-a-half drag gesture emulates the left mouse button. + For example, use the xf86-input-evdev driver for an X pointing device. + + To compile this driver as a module, choose M here: the + module will be called navpoint. + endif diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 4718effeb8d9..46ba7556fd4f 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o obj-$(CONFIG_MOUSE_INPORT) += inport.o obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o +obj-$(CONFIG_MOUSE_NAVPOINT_PXA27x) += navpoint.o obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o obj-$(CONFIG_MOUSE_PS2) += psmouse.o obj-$(CONFIG_MOUSE_PXA930_TRKBALL) += pxa930_trkball.o diff --git a/drivers/input/mouse/navpoint.c b/drivers/input/mouse/navpoint.c new file mode 100644 index 000000000000..c29ae7654d5e --- /dev/null +++ b/drivers/input/mouse/navpoint.c @@ -0,0 +1,369 @@ +/* + * Synaptics NavPoint (PXA27x SSP/SPI) driver. + * + * Copyright (C) 2012 Paul Parsons + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Synaptics Modular Embedded Protocol: Module Packet Format. + * Module header byte 2:0 = Length (# bytes that follow) + * Module header byte 4:3 = Control + * Module header byte 7:5 = Module Address + */ +#define HEADER_LENGTH(byte) ((byte) & 0x07) +#define HEADER_CONTROL(byte) (((byte) >> 3) & 0x03) +#define HEADER_ADDRESS(byte) ((byte) >> 5) + +struct navpoint { + struct ssp_device *ssp; + struct input_dev *input; + struct device *dev; + int gpio; + int index; + u8 data[1 + HEADER_LENGTH(0xff)]; +}; + +/* + * Initialization values for SSCR0_x, SSCR1_x, SSSR_x. + */ +static const u32 sscr0 = 0 + | SSCR0_TUM /* TIM = 1; No TUR interrupts */ + | SSCR0_RIM /* RIM = 1; No ROR interrupts */ + | SSCR0_SSE /* SSE = 1; SSP enabled */ + | SSCR0_Motorola /* FRF = 0; Motorola SPI */ + | SSCR0_DataSize(16) /* DSS = 15; Data size = 16-bit */ + ; +static const u32 sscr1 = 0 + | SSCR1_SCFR /* SCFR = 1; SSPSCLK only during transfers */ + | SSCR1_SCLKDIR /* SCLKDIR = 1; Slave mode */ + | SSCR1_SFRMDIR /* SFRMDIR = 1; Slave mode */ + | SSCR1_RWOT /* RWOT = 1; Receive without transmit mode */ + | SSCR1_RxTresh(1) /* RFT = 0; Receive FIFO threshold = 1 */ + | SSCR1_SPH /* SPH = 1; SSPSCLK inactive 0.5 + 1 cycles */ + | SSCR1_RIE /* RIE = 1; Receive FIFO interrupt enabled */ + ; +static const u32 sssr = 0 + | SSSR_BCE /* BCE = 1; Clear BCE */ + | SSSR_TUR /* TUR = 1; Clear TUR */ + | SSSR_EOC /* EOC = 1; Clear EOC */ + | SSSR_TINT /* TINT = 1; Clear TINT */ + | SSSR_PINT /* PINT = 1; Clear PINT */ + | SSSR_ROR /* ROR = 1; Clear ROR */ + ; + +/* + * MEP Query $22: Touchpad Coordinate Range Query is not supported by + * the NavPoint module, so sampled values provide the default limits. + */ +#define NAVPOINT_X_MIN 1278 +#define NAVPOINT_X_MAX 5340 +#define NAVPOINT_Y_MIN 1572 +#define NAVPOINT_Y_MAX 4396 +#define NAVPOINT_PRESSURE_MIN 0 +#define NAVPOINT_PRESSURE_MAX 255 + +static void navpoint_packet(struct navpoint *navpoint) +{ + int finger; + int gesture; + int x, y, z; + + switch (navpoint->data[0]) { + case 0xff: /* Garbage (packet?) between reset and Hello packet */ + case 0x00: /* Module 0, NULL packet */ + break; + + case 0x0e: /* Module 0, Absolute packet */ + finger = (navpoint->data[1] & 0x01); + gesture = (navpoint->data[1] & 0x02); + x = ((navpoint->data[2] & 0x1f) << 8) | navpoint->data[3]; + y = ((navpoint->data[4] & 0x1f) << 8) | navpoint->data[5]; + z = navpoint->data[6]; + input_report_key(navpoint->input, BTN_TOUCH, finger); + input_report_abs(navpoint->input, ABS_X, x); + input_report_abs(navpoint->input, ABS_Y, y); + input_report_abs(navpoint->input, ABS_PRESSURE, z); + input_report_key(navpoint->input, BTN_TOOL_FINGER, finger); + input_report_key(navpoint->input, BTN_LEFT, gesture); + input_sync(navpoint->input); + break; + + case 0x19: /* Module 0, Hello packet */ + if ((navpoint->data[1] & 0xf0) == 0x10) + break; + /* FALLTHROUGH */ + default: + dev_warn(navpoint->dev, + "spurious packet: data=0x%02x,0x%02x,...\n", + navpoint->data[0], navpoint->data[1]); + break; + } +} + +static irqreturn_t navpoint_irq(int irq, void *dev_id) +{ + struct navpoint *navpoint = dev_id; + struct ssp_device *ssp = navpoint->ssp; + irqreturn_t ret = IRQ_NONE; + u32 status; + + status = pxa_ssp_read_reg(ssp, SSSR); + if (status & sssr) { + dev_warn(navpoint->dev, + "unexpected interrupt: status=0x%08x\n", status); + pxa_ssp_write_reg(ssp, SSSR, (status & sssr)); + ret = IRQ_HANDLED; + } + + while (status & SSSR_RNE) { + u32 data; + + data = pxa_ssp_read_reg(ssp, SSDR); + navpoint->data[navpoint->index + 0] = (data >> 8); + navpoint->data[navpoint->index + 1] = data; + navpoint->index += 2; + if (HEADER_LENGTH(navpoint->data[0]) < navpoint->index) { + navpoint_packet(navpoint); + navpoint->index = 0; + } + status = pxa_ssp_read_reg(ssp, SSSR); + ret = IRQ_HANDLED; + } + + return ret; +} + +static void navpoint_up(struct navpoint *navpoint) +{ + struct ssp_device *ssp = navpoint->ssp; + int timeout; + + clk_prepare_enable(ssp->clk); + + pxa_ssp_write_reg(ssp, SSCR1, sscr1); + pxa_ssp_write_reg(ssp, SSSR, sssr); + pxa_ssp_write_reg(ssp, SSTO, 0); + pxa_ssp_write_reg(ssp, SSCR0, sscr0); /* SSCR0_SSE written last */ + + /* Wait until SSP port is ready for slave clock operations */ + for (timeout = 100; timeout != 0; --timeout) { + if (!(pxa_ssp_read_reg(ssp, SSSR) & SSSR_CSS)) + break; + msleep(1); + } + + if (timeout == 0) + dev_err(navpoint->dev, + "timeout waiting for SSSR[CSS] to clear\n"); + + if (gpio_is_valid(navpoint->gpio)) + gpio_set_value(navpoint->gpio, 1); +} + +static void navpoint_down(struct navpoint *navpoint) +{ + struct ssp_device *ssp = navpoint->ssp; + + if (gpio_is_valid(navpoint->gpio)) + gpio_set_value(navpoint->gpio, 0); + + pxa_ssp_write_reg(ssp, SSCR0, 0); + + clk_disable_unprepare(ssp->clk); +} + +static int navpoint_open(struct input_dev *input) +{ + struct navpoint *navpoint = input_get_drvdata(input); + + navpoint_up(navpoint); + + return 0; +} + +static void navpoint_close(struct input_dev *input) +{ + struct navpoint *navpoint = input_get_drvdata(input); + + navpoint_down(navpoint); +} + +static int __devinit navpoint_probe(struct platform_device *pdev) +{ + const struct navpoint_platform_data *pdata = + dev_get_platdata(&pdev->dev); + struct ssp_device *ssp; + struct input_dev *input; + struct navpoint *navpoint; + int error; + + if (!pdata) { + dev_err(&pdev->dev, "no platform data\n"); + return -EINVAL; + } + + if (gpio_is_valid(pdata->gpio)) { + error = gpio_request_one(pdata->gpio, GPIOF_OUT_INIT_LOW, + "SYNAPTICS_ON"); + if (error) + return error; + } + + ssp = pxa_ssp_request(pdata->port, pdev->name); + if (!ssp) { + error = -ENODEV; + goto err_free_gpio; + } + + /* HaRET does not disable devices before jumping into Linux */ + if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) { + pxa_ssp_write_reg(ssp, SSCR0, 0); + dev_warn(&pdev->dev, "ssp%d already enabled\n", pdata->port); + } + + navpoint = kzalloc(sizeof(*navpoint), GFP_KERNEL); + input = input_allocate_device(); + if (!navpoint || !input) { + error = -ENOMEM; + goto err_free_mem; + } + + navpoint->ssp = ssp; + navpoint->input = input; + navpoint->dev = &pdev->dev; + navpoint->gpio = pdata->gpio; + + input->name = pdev->name; + input->dev.parent = &pdev->dev; + + __set_bit(EV_KEY, input->evbit); + __set_bit(EV_ABS, input->evbit); + __set_bit(BTN_LEFT, input->keybit); + __set_bit(BTN_TOUCH, input->keybit); + __set_bit(BTN_TOOL_FINGER, input->keybit); + + input_set_abs_params(input, ABS_X, + NAVPOINT_X_MIN, NAVPOINT_X_MAX, 0, 0); + input_set_abs_params(input, ABS_Y, + NAVPOINT_Y_MIN, NAVPOINT_Y_MAX, 0, 0); + input_set_abs_params(input, ABS_PRESSURE, + NAVPOINT_PRESSURE_MIN, NAVPOINT_PRESSURE_MAX, + 0, 0); + + input->open = navpoint_open; + input->close = navpoint_close; + + input_set_drvdata(input, navpoint); + + error = request_irq(ssp->irq, navpoint_irq, 0, pdev->name, navpoint); + if (error) + goto err_free_mem; + + error = input_register_device(input); + if (error) + goto err_free_irq; + + platform_set_drvdata(pdev, navpoint); + dev_dbg(&pdev->dev, "ssp%d, irq %d\n", pdata->port, ssp->irq); + + return 0; + +err_free_irq: + free_irq(ssp->irq, &pdev->dev); +err_free_mem: + input_free_device(input); + kfree(navpoint); + pxa_ssp_free(ssp); +err_free_gpio: + if (gpio_is_valid(pdata->gpio)) + gpio_free(pdata->gpio); + + return error; +} + +static int __devexit navpoint_remove(struct platform_device *pdev) +{ + const struct navpoint_platform_data *pdata = + dev_get_platdata(&pdev->dev); + struct navpoint *navpoint = platform_get_drvdata(pdev); + struct ssp_device *ssp = navpoint->ssp; + + free_irq(ssp->irq, navpoint); + + input_unregister_device(navpoint->input); + kfree(navpoint); + + pxa_ssp_free(ssp); + + if (gpio_is_valid(pdata->gpio)) + gpio_free(pdata->gpio); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int navpoint_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct navpoint *navpoint = platform_get_drvdata(pdev); + struct input_dev *input = navpoint->input; + + mutex_lock(&input->mutex); + if (input->users) + navpoint_down(navpoint); + mutex_unlock(&input->mutex); + + return 0; +} + +static int navpoint_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct navpoint *navpoint = platform_get_drvdata(pdev); + struct input_dev *input = navpoint->input; + + mutex_lock(&input->mutex); + if (input->users) + navpoint_up(navpoint); + mutex_unlock(&input->mutex); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(navpoint_pm_ops, navpoint_suspend, navpoint_resume); + +static struct platform_driver navpoint_driver = { + .probe = navpoint_probe, + .remove = __devexit_p(navpoint_remove), + .driver = { + .name = "navpoint", + .owner = THIS_MODULE, + .pm = &navpoint_pm_ops, + }, +}; + +module_platform_driver(navpoint_driver); + +MODULE_AUTHOR("Paul Parsons "); +MODULE_DESCRIPTION("Synaptics NavPoint (PXA27x SSP/SPI) driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:navpoint"); diff --git a/include/linux/input/navpoint.h b/include/linux/input/navpoint.h new file mode 100644 index 000000000000..45050eb34de3 --- /dev/null +++ b/include/linux/input/navpoint.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2012 Paul Parsons + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +struct navpoint_platform_data { + int port; /* PXA SSP port for pxa_ssp_request() */ + int gpio; /* GPIO for power on/off */ +}; -- cgit v1.2.3 From 1932811f426fee71b7ece67e70aeba7e1b0ebb6d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 10 May 2012 22:37:08 -0700 Subject: Input: matrix-keymap - uninline and prepare for device tree support Change matrix-keymap helper to be out-of-line, like sparse keymap, allow the helper perform basic keymap validation and return errors, and prepare for device tree support. Signed-off-by: Dmitry Torokhov --- drivers/input/Kconfig | 17 +++- drivers/input/Makefile | 2 +- drivers/input/keyboard/Kconfig | 18 +++- drivers/input/keyboard/ep93xx_keypad.c | 18 ++-- drivers/input/keyboard/imx_keypad.c | 17 ++-- drivers/input/keyboard/lm8333.c | 15 ++- drivers/input/keyboard/matrix_keypad.c | 17 ++-- drivers/input/keyboard/nomadik-ske-keypad.c | 20 ++-- drivers/input/keyboard/omap-keypad.c | 20 ++-- drivers/input/keyboard/omap4-keypad.c | 15 ++- drivers/input/keyboard/pmic8xxx-keypad.c | 20 ++-- drivers/input/keyboard/samsung-keypad.c | 20 ++-- drivers/input/keyboard/spear-keyboard.c | 21 ++-- drivers/input/keyboard/stmpe-keypad.c | 16 ++-- drivers/input/keyboard/tc3589x-keypad.c | 19 ++-- drivers/input/keyboard/tca8418_keypad.c | 15 ++- drivers/input/keyboard/tegra-kbc.c | 34 ++++--- drivers/input/keyboard/tnetv107x-keypad.c | 21 ++-- drivers/input/keyboard/twl4030_keypad.c | 25 +++-- drivers/input/keyboard/w90p910_keypad.c | 27 +++--- drivers/input/matrix-keymap.c | 142 ++++++++++++++++++++++++++++ drivers/input/of_keymap.c | 87 ----------------- include/linux/input/matrix_keypad.h | 38 ++------ 23 files changed, 350 insertions(+), 294 deletions(-) create mode 100644 drivers/input/matrix-keymap.c delete mode 100644 drivers/input/of_keymap.c (limited to 'include/linux/input') diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 332597980817..55f7e57d4e42 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -25,10 +25,6 @@ config INPUT if INPUT -config INPUT_OF_MATRIX_KEYMAP - depends on USE_OF - bool - config INPUT_FF_MEMLESS tristate "Support for memoryless force-feedback devices" help @@ -68,6 +64,19 @@ config INPUT_SPARSEKMAP To compile this driver as a module, choose M here: the module will be called sparse-keymap. +config INPUT_MATRIXKMAP + tristate "Matrix keymap support library" + help + Say Y here if you are using a driver for an input + device that uses matrix keymap. This option is only + useful for out-of-tree drivers since in-tree drivers + select it automatically. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called matrix-keymap. + comment "Userland interfaces" config INPUT_MOUSEDEV diff --git a/drivers/input/Makefile b/drivers/input/Makefile index b173a13a73ca..5ca3f631497f 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -10,6 +10,7 @@ input-core-y := input.o input-compat.o input-mt.o ff-core.o obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o obj-$(CONFIG_INPUT_SPARSEKMAP) += sparse-keymap.o +obj-$(CONFIG_INPUT_MATRIXKMAP) += matrix-keymap.o obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o obj-$(CONFIG_INPUT_JOYDEV) += joydev.o @@ -24,4 +25,3 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o -obj-$(CONFIG_INPUT_OF_MATRIX_KEYMAP) += of_keymap.o diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index ea4bbaae9e02..c0e11ecc646f 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -166,6 +166,7 @@ config KEYBOARD_LKKBD config KEYBOARD_EP93XX tristate "EP93xx Matrix Keypad support" depends on ARCH_EP93XX + select INPUT_MATRIXKMAP help Say Y here to enable the matrix keypad on the Cirrus EP93XX. @@ -224,6 +225,7 @@ config KEYBOARD_TCA6416 config KEYBOARD_TCA8418 tristate "TCA8418 Keypad Support" depends on I2C + select INPUT_MATRIXKMAP help This driver implements basic keypad functionality for keys connected through TCA8418 keypad decoder. @@ -240,6 +242,7 @@ config KEYBOARD_TCA8418 config KEYBOARD_MATRIX tristate "GPIO driven matrix keypad support" depends on GENERIC_GPIO + select INPUT_MATRIXKMAP help Enable support for GPIO driven matrix keypad. @@ -312,6 +315,7 @@ config KEYBOARD_LM8323 config KEYBOARD_LM8333 tristate "LM8333 keypad chip" depends on I2C + select INPUT_MATRIXKMAP help If you say yes here you get support for the National Semiconductor LM8333 keypad controller. @@ -376,6 +380,7 @@ config KEYBOARD_MPR121 config KEYBOARD_IMX tristate "IMX keypad support" depends on ARCH_MXC + select INPUT_MATRIXKMAP help Enable support for IMX keypad port. @@ -394,6 +399,7 @@ config KEYBOARD_NEWTON config KEYBOARD_NOMADIK tristate "ST-Ericsson Nomadik SKE keyboard" depends on PLAT_NOMADIK + select INPUT_MATRIXKMAP help Say Y here if you want to use a keypad provided on the SKE controller used on the Ux500 and Nomadik platforms @@ -404,7 +410,7 @@ config KEYBOARD_NOMADIK config KEYBOARD_TEGRA tristate "NVIDIA Tegra internal matrix keyboard controller support" depends on ARCH_TEGRA - select INPUT_OF_MATRIX_KEYMAP if USE_OF + select INPUT_MATRIXKMAP help Say Y here if you want to use a matrix keyboard connected directly to the internal keyboard controller on Tegra SoCs. @@ -442,6 +448,7 @@ config KEYBOARD_PXA930_ROTARY config KEYBOARD_PMIC8XXX tristate "Qualcomm PMIC8XXX keypad support" depends on MFD_PM8XXX + select INPUT_MATRIXKMAP help Say Y here if you want to enable the driver for the PMIC8XXX keypad provided as a reference design from Qualcomm. This is intended @@ -453,6 +460,7 @@ config KEYBOARD_PMIC8XXX config KEYBOARD_SAMSUNG tristate "Samsung keypad support" depends on HAVE_CLK + select INPUT_MATRIXKMAP help Say Y here if you want to use the keypad on your Samsung mobile device. @@ -495,6 +503,7 @@ config KEYBOARD_SH_KEYSC config KEYBOARD_STMPE tristate "STMPE keypad support" depends on MFD_STMPE + select INPUT_MATRIXKMAP help Say Y here if you want to use the keypad controller on STMPE I/O expanders. @@ -515,6 +524,7 @@ config KEYBOARD_DAVINCI config KEYBOARD_OMAP tristate "TI OMAP keypad support" depends on (ARCH_OMAP1 || ARCH_OMAP2) + select INPUT_MATRIXKMAP help Say Y here if you want to use the OMAP keypad. @@ -523,6 +533,7 @@ config KEYBOARD_OMAP config KEYBOARD_OMAP4 tristate "TI OMAP4+ keypad support" + select INPUT_MATRIXKMAP help Say Y here if you want to use the OMAP4+ keypad. @@ -532,6 +543,7 @@ config KEYBOARD_OMAP4 config KEYBOARD_SPEAR tristate "ST SPEAR keyboard support" depends on PLAT_SPEAR + select INPUT_MATRIXKMAP help Say Y here if you want to use the SPEAR keyboard. @@ -541,6 +553,7 @@ config KEYBOARD_SPEAR config KEYBOARD_TC3589X tristate "TC3589X Keypad support" depends on MFD_TC3589X + select INPUT_MATRIXKMAP help Say Y here if you want to use the keypad controller on TC35892/3 I/O expander. @@ -551,6 +564,7 @@ config KEYBOARD_TC3589X config KEYBOARD_TNETV107X tristate "TI TNETV107X keypad support" depends on ARCH_DAVINCI_TNETV107X + select INPUT_MATRIXKMAP help Say Y here if you want to use the TNETV107X keypad. @@ -560,6 +574,7 @@ config KEYBOARD_TNETV107X config KEYBOARD_TWL4030 tristate "TI TWL4030/TWL5030/TPS659x0 keypad support" depends on TWL4030_CORE + select INPUT_MATRIXKMAP help Say Y here if your board use the keypad controller on TWL4030 family chips. It's safe to say enable this @@ -583,6 +598,7 @@ config KEYBOARD_XTKBD config KEYBOARD_W90P910 tristate "W90P910 Matrix Keypad support" depends on ARCH_W90X900 + select INPUT_MATRIXKMAP help Say Y here to enable the matrix keypad on evaluation board based on W90P910. diff --git a/drivers/input/keyboard/ep93xx_keypad.c b/drivers/input/keyboard/ep93xx_keypad.c index df194bdaab50..c46fc8185469 100644 --- a/drivers/input/keyboard/ep93xx_keypad.c +++ b/drivers/input/keyboard/ep93xx_keypad.c @@ -303,19 +303,16 @@ static int __devinit ep93xx_keypad_probe(struct platform_device *pdev) input_dev->open = ep93xx_keypad_open; input_dev->close = ep93xx_keypad_close; input_dev->dev.parent = &pdev->dev; - input_dev->keycode = keypad->keycodes; - input_dev->keycodesize = sizeof(keypad->keycodes[0]); - input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); - input_set_drvdata(input_dev, keypad); + err = matrix_keypad_build_keymap(keymap_data, NULL, + EP93XX_MATRIX_ROWS, EP93XX_MATRIX_COLS, + keypad->keycodes, input_dev); + if (err) + goto failed_free_dev; - input_dev->evbit[0] = BIT_MASK(EV_KEY); if (keypad->pdata->flags & EP93XX_KEYPAD_AUTOREPEAT) - input_dev->evbit[0] |= BIT_MASK(EV_REP); - - matrix_keypad_build_keymap(keymap_data, 3, - input_dev->keycode, input_dev->keybit); - platform_set_drvdata(pdev, keypad); + __set_bit(EV_REP, input_dev->evbit); + input_set_drvdata(input_dev, keypad); err = request_irq(keypad->irq, ep93xx_keypad_irq_handler, 0, pdev->name, keypad); @@ -326,6 +323,7 @@ static int __devinit ep93xx_keypad_probe(struct platform_device *pdev) if (err) goto failed_free_irq; + platform_set_drvdata(pdev, keypad); device_init_wakeup(&pdev->dev, 1); return 0; diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index fb87b3bcadb9..6ee7421e2321 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c @@ -481,7 +481,7 @@ static int __devinit imx_keypad_probe(struct platform_device *pdev) } if (keypad->rows_en_mask > ((1 << MAX_MATRIX_KEY_ROWS) - 1) || - keypad->cols_en_mask > ((1 << MAX_MATRIX_KEY_COLS) - 1)) { + keypad->cols_en_mask > ((1 << MAX_MATRIX_KEY_COLS) - 1)) { dev_err(&pdev->dev, "invalid key data (too many rows or colums)\n"); error = -EINVAL; @@ -496,14 +496,17 @@ static int __devinit imx_keypad_probe(struct platform_device *pdev) input_dev->dev.parent = &pdev->dev; input_dev->open = imx_keypad_open; input_dev->close = imx_keypad_close; - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); - input_dev->keycode = keypad->keycodes; - input_dev->keycodesize = sizeof(keypad->keycodes[0]); - input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); - matrix_keypad_build_keymap(keymap_data, MATRIX_ROW_SHIFT, - keypad->keycodes, input_dev->keybit); + error = matrix_keypad_build_keymap(keymap_data, NULL, + MAX_MATRIX_KEY_ROWS, + MAX_MATRIX_KEY_COLS, + keypad->keycodes, input_dev); + if (error) { + dev_err(&pdev->dev, "failed to build keymap\n"); + goto failed_clock_put; + } + __set_bit(EV_REP, input_dev->evbit); input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_set_drvdata(input_dev, keypad); diff --git a/drivers/input/keyboard/lm8333.c b/drivers/input/keyboard/lm8333.c index 9a8c4a6cf5c6..ca168a6679de 100644 --- a/drivers/input/keyboard/lm8333.c +++ b/drivers/input/keyboard/lm8333.c @@ -29,9 +29,9 @@ #define LM8333_FIFO_TRANSFER_SIZE 16 -#define LM8333_ROW_SHIFT 4 #define LM8333_NUM_ROWS 8 - +#define LM8333_NUM_COLS 16 +#define LM8333_ROW_SHIFT 4 struct lm8333 { struct i2c_client *client; @@ -159,14 +159,13 @@ static int __devinit lm8333_probe(struct i2c_client *client, input->dev.parent = &client->dev; input->id.bustype = BUS_I2C; - input->keycode = lm8333->keycodes; - input->keycodesize = sizeof(lm8333->keycodes[0]); - input->keycodemax = ARRAY_SIZE(lm8333->keycodes); - input->evbit[0] = BIT_MASK(EV_KEY); input_set_capability(input, EV_MSC, MSC_SCAN); - matrix_keypad_build_keymap(pdata->matrix_data, LM8333_ROW_SHIFT, - input->keycode, input->keybit); + err = matrix_keypad_build_keymap(pdata->matrix_data, NULL, + LM8333_NUM_ROWS, LM8333_NUM_COLS, + lm8333->keycodes, input); + if (err) + goto free_mem; if (pdata->debounce_time) { err = lm8333_write8(lm8333, LM8333_DEBOUNCE, diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index 98ae281bedb0..18b72372028a 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c @@ -437,19 +437,18 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev) input_dev->name = pdev->name; input_dev->id.bustype = BUS_HOST; input_dev->dev.parent = &pdev->dev; - input_dev->evbit[0] = BIT_MASK(EV_KEY); - if (!pdata->no_autorepeat) - input_dev->evbit[0] |= BIT_MASK(EV_REP); input_dev->open = matrix_keypad_start; input_dev->close = matrix_keypad_stop; - input_dev->keycode = keypad->keycodes; - input_dev->keycodesize = sizeof(keypad->keycodes[0]); - input_dev->keycodemax = pdata->num_row_gpios << row_shift; - - matrix_keypad_build_keymap(keymap_data, row_shift, - input_dev->keycode, input_dev->keybit); + err = matrix_keypad_build_keymap(keymap_data, NULL, + pdata->num_row_gpios, + pdata->num_col_gpios, + keypad->keycodes, input_dev); + if (err) + goto err_free_mem; + if (!pdata->no_autorepeat) + __set_bit(EV_REP, input_dev->evbit); input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_set_drvdata(input_dev, keypad); diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 101e245944e7..4ea4341a68c5 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -39,7 +39,8 @@ #define SKE_KPRISA (0x1 << 2) #define SKE_KEYPAD_ROW_SHIFT 3 -#define SKE_KPD_KEYMAP_SIZE (8 * 8) +#define SKE_KPD_NUM_ROWS 8 +#define SKE_KPD_NUM_COLS 8 /* keypad auto scan registers */ #define SKE_ASR0 0x20 @@ -63,7 +64,7 @@ struct ske_keypad { void __iomem *reg_base; struct input_dev *input; const struct ske_keypad_platform_data *board; - unsigned short keymap[SKE_KPD_KEYMAP_SIZE]; + unsigned short keymap[SKE_KPD_NUM_ROWS * SKE_KPD_NUM_COLS]; struct clk *clk; spinlock_t ske_keypad_lock; }; @@ -261,19 +262,18 @@ static int __init ske_keypad_probe(struct platform_device *pdev) input->name = "ux500-ske-keypad"; input->dev.parent = &pdev->dev; - input->keycode = keypad->keymap; - input->keycodesize = sizeof(keypad->keymap[0]); - input->keycodemax = ARRAY_SIZE(keypad->keymap); + error = matrix_keypad_build_keymap(plat->keymap_data, NULL, + SKE_KPD_NUM_ROWS, SKE_KPD_NUM_COLS, + keypad->keymap, input); + if (error) { + dev_err(&pdev->dev, "Failed to build keymap\n"); + goto err_iounmap; + } input_set_capability(input, EV_MSC, MSC_SCAN); - - __set_bit(EV_KEY, input->evbit); if (!plat->no_autorepeat) __set_bit(EV_REP, input->evbit); - matrix_keypad_build_keymap(plat->keymap_data, SKE_KEYPAD_ROW_SHIFT, - input->keycode, input->keybit); - clk_enable(keypad->clk); /* go through board initialization helpers */ diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index 6b630d9d3dff..a0222db4dc86 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -61,6 +61,7 @@ struct omap_kp { unsigned int cols; unsigned long delay; unsigned int debounce; + unsigned short keymap[]; }; static DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0); @@ -316,13 +317,6 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) if (!cpu_is_omap24xx()) omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); - input_dev->keycode = &omap_kp[1]; - input_dev->keycodesize = sizeof(unsigned short); - input_dev->keycodemax = keycodemax; - - if (pdata->rep) - __set_bit(EV_REP, input_dev->evbit); - if (pdata->delay) omap_kp->delay = pdata->delay; @@ -371,9 +365,6 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) goto err2; /* setup input device */ - __set_bit(EV_KEY, input_dev->evbit); - matrix_keypad_build_keymap(pdata->keymap_data, row_shift, - input_dev->keycode, input_dev->keybit); input_dev->name = "omap-keypad"; input_dev->phys = "omap-keypad/input0"; input_dev->dev.parent = &pdev->dev; @@ -383,6 +374,15 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; + if (pdata->rep) + __set_bit(EV_REP, input_dev->evbit); + + ret = matrix_keypad_build_keymap(pdata->keymap_data, NULL, + pdata->rows, pdata->cols, + omap_kp->keymap, input_dev); + if (ret < 0) + goto err3; + ret = input_register_device(omap_kp->input); if (ret < 0) { printk(KERN_ERR "Unable to register omap-keypad input device\n"); diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 28da0e469689..aed5f6999ce2 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -322,20 +322,19 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev) input_dev->open = omap4_keypad_open; input_dev->close = omap4_keypad_close; - input_dev->keycode = keypad_data->keymap; - input_dev->keycodesize = sizeof(keypad_data->keymap[0]); - input_dev->keycodemax = max_keys; + error = matrix_keypad_build_keymap(pdata->keymap_data, NULL, + pdata->rows, pdata->cols, + keypad_data->keymap, input_dev); + if (error) { + dev_err(&pdev->dev, "failed to build keymap\n"); + goto err_free_input; + } - __set_bit(EV_KEY, input_dev->evbit); __set_bit(EV_REP, input_dev->evbit); - input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_set_drvdata(input_dev, keypad_data); - matrix_keypad_build_keymap(pdata->keymap_data, row_shift, - input_dev->keycode, input_dev->keybit); - error = request_irq(keypad_data->irq, omap4_keypad_interrupt, IRQF_TRIGGER_RISING, "omap4-keypad", keypad_data); diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c index 01a1c9f8a383..52c34657d301 100644 --- a/drivers/input/keyboard/pmic8xxx-keypad.c +++ b/drivers/input/keyboard/pmic8xxx-keypad.c @@ -626,21 +626,21 @@ static int __devinit pmic8xxx_kp_probe(struct platform_device *pdev) kp->input->id.product = 0x0001; kp->input->id.vendor = 0x0001; - kp->input->evbit[0] = BIT_MASK(EV_KEY); - - if (pdata->rep) - __set_bit(EV_REP, kp->input->evbit); - - kp->input->keycode = kp->keycodes; - kp->input->keycodemax = PM8XXX_MATRIX_MAX_SIZE; - kp->input->keycodesize = sizeof(kp->keycodes); kp->input->open = pmic8xxx_kp_open; kp->input->close = pmic8xxx_kp_close; - matrix_keypad_build_keymap(keymap_data, PM8XXX_ROW_SHIFT, - kp->input->keycode, kp->input->keybit); + rc = matrix_keypad_build_keymap(keymap_data, NULL, + PM8XXX_MAX_ROWS, PM8XXX_MAX_COLS, + kp->keycodes, kp->input); + if (rc) { + dev_err(&pdev->dev, "failed to build keymap\n"); + goto err_get_irq; + } + if (pdata->rep) + __set_bit(EV_REP, kp->input->evbit); input_set_capability(kp->input, EV_MSC, MSC_SCAN); + input_set_drvdata(kp->input, kp); /* initialize keypad state */ diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index 2391ae884fee..a061ba603a29 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -454,23 +454,23 @@ static int __devinit samsung_keypad_probe(struct platform_device *pdev) input_dev->name = pdev->name; input_dev->id.bustype = BUS_HOST; input_dev->dev.parent = &pdev->dev; - input_set_drvdata(input_dev, keypad); input_dev->open = samsung_keypad_open; input_dev->close = samsung_keypad_close; - input_dev->evbit[0] = BIT_MASK(EV_KEY); - if (!pdata->no_autorepeat) - input_dev->evbit[0] |= BIT_MASK(EV_REP); + error = matrix_keypad_build_keymap(keymap_data, NULL, + pdata->rows, pdata->cols, + keypad->keycodes, input_dev); + if (error) { + dev_err(&pdev->dev, "failed to build keymap\n"); + goto err_put_clk; + } input_set_capability(input_dev, EV_MSC, MSC_SCAN); + if (!pdata->no_autorepeat) + __set_bit(EV_REP, input_dev->evbit); - input_dev->keycode = keypad->keycodes; - input_dev->keycodesize = sizeof(keypad->keycodes[0]); - input_dev->keycodemax = pdata->rows << row_shift; - - matrix_keypad_build_keymap(keymap_data, row_shift, - input_dev->keycode, input_dev->keybit); + input_set_drvdata(input_dev, keypad); keypad->irq = platform_get_irq(pdev, 0); if (keypad->irq < 0) { diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index 3b6b528f02fd..e83cab27eb56 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -49,7 +49,9 @@ #define KEY_VALUE 0x00FFFFFF #define ROW_MASK 0xF0 #define COLUMN_MASK 0x0F -#define ROW_SHIFT 4 +#define NUM_ROWS 16 +#define NUM_COLS 16 + #define KEY_MATRIX_SHIFT 6 struct spear_kbd { @@ -60,7 +62,7 @@ struct spear_kbd { unsigned int irq; unsigned int mode; unsigned short last_key; - unsigned short keycodes[256]; + unsigned short keycodes[NUM_ROWS * NUM_COLS]; }; static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id) @@ -212,18 +214,17 @@ static int __devinit spear_kbd_probe(struct platform_device *pdev) input_dev->open = spear_kbd_open; input_dev->close = spear_kbd_close; - __set_bit(EV_KEY, input_dev->evbit); + error = matrix_keypad_build_keymap(keymap, NULL, NUM_ROWS, NUM_COLS, + kbd->keycodes, input_dev); + if (error) { + dev_err(&pdev->dev, "Failed to build keymap\n"); + goto err_put_clk; + } + if (pdata->rep) __set_bit(EV_REP, input_dev->evbit); input_set_capability(input_dev, EV_MSC, MSC_SCAN); - input_dev->keycode = kbd->keycodes; - input_dev->keycodesize = sizeof(kbd->keycodes[0]); - input_dev->keycodemax = ARRAY_SIZE(kbd->keycodes); - - matrix_keypad_build_keymap(keymap, ROW_SHIFT, - input_dev->keycode, input_dev->keybit); - input_set_drvdata(input_dev, kbd); error = request_irq(irq, spear_kbd_interrupt, 0, "keyboard", kbd); diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c index 9397cf9c625c..470a8778dec1 100644 --- a/drivers/input/keyboard/stmpe-keypad.c +++ b/drivers/input/keyboard/stmpe-keypad.c @@ -289,19 +289,17 @@ static int __devinit stmpe_keypad_probe(struct platform_device *pdev) input->id.bustype = BUS_I2C; input->dev.parent = &pdev->dev; - input_set_capability(input, EV_MSC, MSC_SCAN); + ret = matrix_keypad_build_keymap(plat->keymap_data, NULL, + STMPE_KEYPAD_MAX_ROWS, + STMPE_KEYPAD_MAX_COLS, + keypad->keymap, input); + if (ret) + goto out_freeinput; - __set_bit(EV_KEY, input->evbit); + input_set_capability(input, EV_MSC, MSC_SCAN); if (!plat->no_autorepeat) __set_bit(EV_REP, input->evbit); - input->keycode = keypad->keymap; - input->keycodesize = sizeof(keypad->keymap[0]); - input->keycodemax = ARRAY_SIZE(keypad->keymap); - - matrix_keypad_build_keymap(plat->keymap_data, STMPE_KEYPAD_ROW_SHIFT, - input->keycode, input->keybit); - for (i = 0; i < plat->keymap_data->keymap_size; i++) { unsigned int key = plat->keymap_data->keymap[i]; diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c index f4da2a7a6970..7d498e698508 100644 --- a/drivers/input/keyboard/tc3589x-keypad.c +++ b/drivers/input/keyboard/tc3589x-keypad.c @@ -78,7 +78,7 @@ * @input: pointer to input device object * @board: keypad platform device * @krow: number of rows - * @kcol: number of coloumns + * @kcol: number of columns * @keymap: matrix scan code table for keycodes * @keypad_stopped: holds keypad status */ @@ -333,23 +333,22 @@ static int __devinit tc3589x_keypad_probe(struct platform_device *pdev) input->name = pdev->name; input->dev.parent = &pdev->dev; - input->keycode = keypad->keymap; - input->keycodesize = sizeof(keypad->keymap[0]); - input->keycodemax = ARRAY_SIZE(keypad->keymap); - input->open = tc3589x_keypad_open; input->close = tc3589x_keypad_close; - input_set_drvdata(input, keypad); + error = matrix_keypad_build_keymap(plat->keymap_data, NULL, + TC3589x_MAX_KPROW, TC3589x_MAX_KPCOL, + keypad->keymap, input); + if (error) { + dev_err(&pdev->dev, "Failed to build keymap\n"); + goto err_free_mem; + } input_set_capability(input, EV_MSC, MSC_SCAN); - - __set_bit(EV_KEY, input->evbit); if (!plat->no_autorepeat) __set_bit(EV_REP, input->evbit); - matrix_keypad_build_keymap(plat->keymap_data, 0x3, - input->keycode, input->keybit); + input_set_drvdata(input, keypad); error = request_threaded_irq(irq, NULL, tc3589x_keypad_irq, plat->irqtype, diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c index 958ec107bfbc..5f87b28b3192 100644 --- a/drivers/input/keyboard/tca8418_keypad.c +++ b/drivers/input/keyboard/tca8418_keypad.c @@ -342,21 +342,20 @@ static int __devinit tca8418_keypad_probe(struct i2c_client *client, input->id.product = 0x001; input->id.version = 0x0001; - input->keycode = keypad_data->keymap; - input->keycodesize = sizeof(keypad_data->keymap[0]); - input->keycodemax = max_keys; + error = matrix_keypad_build_keymap(pdata->keymap_data, NULL, + pdata->rows, pdata->cols, + keypad_data->keymap, input); + if (error) { + dev_dbg(&client->dev, "Failed to build keymap\n"); + goto fail2; + } - __set_bit(EV_KEY, input->evbit); if (pdata->rep) __set_bit(EV_REP, input->evbit); - input_set_capability(input, EV_MSC, MSC_SCAN); input_set_drvdata(input, keypad_data); - matrix_keypad_build_keymap(pdata->keymap_data, row_shift, - input->keycode, input->keybit); - if (pdata->irq_is_gpio) client->irq = gpio_to_irq(client->irq); diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index fe4ac95ca6c8..6722d376e898 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -686,6 +686,7 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) int num_rows = 0; unsigned int debounce_cnt; unsigned int scan_time_rows; + unsigned int keymap_rows; if (!pdata) pdata = tegra_kbc_dt_parse_pdata(pdev); @@ -757,29 +758,34 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + pdata->repeat_cnt; kbc->repoll_dly = DIV_ROUND_UP(kbc->repoll_dly, KBC_CYCLE_MS); + kbc->wakeup_key = pdata->wakeup_key; + kbc->use_fn_map = pdata->use_fn_map; + kbc->use_ghost_filter = pdata->use_ghost_filter; + input_dev->name = pdev->name; input_dev->id.bustype = BUS_HOST; input_dev->dev.parent = &pdev->dev; input_dev->open = tegra_kbc_open; input_dev->close = tegra_kbc_close; - input_set_drvdata(input_dev, kbc); - - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); - input_set_capability(input_dev, EV_MSC, MSC_SCAN); - - input_dev->keycode = kbc->keycode; - input_dev->keycodesize = sizeof(kbc->keycode[0]); - input_dev->keycodemax = KBC_MAX_KEY; + keymap_rows = KBC_MAX_KEY; if (pdata->use_fn_map) - input_dev->keycodemax *= 2; + keymap_rows *= 2; - kbc->use_fn_map = pdata->use_fn_map; - kbc->use_ghost_filter = pdata->use_ghost_filter; keymap_data = pdata->keymap_data ?: &tegra_kbc_default_keymap_data; - matrix_keypad_build_keymap(keymap_data, KBC_ROW_SHIFT, - input_dev->keycode, input_dev->keybit); - kbc->wakeup_key = pdata->wakeup_key; + + err = matrix_keypad_build_keymap(keymap_data, NULL, + keymap_rows, KBC_MAX_COL, + kbc->keycode, input_dev); + if (err) { + dev_err(&pdev->dev, "failed to build keymap\n"); + goto err_put_clk; + } + + __set_bit(EV_REP, input_dev->evbit); + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + input_set_drvdata(input_dev, kbc); err = request_irq(kbc->irq, tegra_kbc_isr, IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH, pdev->name, kbc); diff --git a/drivers/input/keyboard/tnetv107x-keypad.c b/drivers/input/keyboard/tnetv107x-keypad.c index fb39c94b6fdd..a4a445fb7020 100644 --- a/drivers/input/keyboard/tnetv107x-keypad.c +++ b/drivers/input/keyboard/tnetv107x-keypad.c @@ -247,15 +247,11 @@ static int __devinit keypad_probe(struct platform_device *pdev) error = -ENOMEM; goto error_input; } - input_set_drvdata(kp->input_dev, kp); kp->input_dev->name = pdev->name; kp->input_dev->dev.parent = &pdev->dev; kp->input_dev->open = keypad_start; kp->input_dev->close = keypad_stop; - kp->input_dev->evbit[0] = BIT_MASK(EV_KEY); - if (!pdata->no_autorepeat) - kp->input_dev->evbit[0] |= BIT_MASK(EV_REP); clk_enable(kp->clk); rev = keypad_read(kp, rev); @@ -264,15 +260,20 @@ static int __devinit keypad_probe(struct platform_device *pdev) kp->input_dev->id.version = ((rev >> 16) & 0xfff); clk_disable(kp->clk); - kp->input_dev->keycode = kp->keycodes; - kp->input_dev->keycodesize = sizeof(kp->keycodes[0]); - kp->input_dev->keycodemax = kp->rows << kp->row_shift; - - matrix_keypad_build_keymap(keymap_data, kp->row_shift, kp->keycodes, - kp->input_dev->keybit); + error = matrix_keypad_build_keymap(keymap_data, NULL, + kp->rows, kp->cols, + kp->keycodes, kp->input_dev); + if (error) { + dev_err(dev, "Failed to build keymap\n"); + goto error_reg; + } + if (!pdata->no_autorepeat) + kp->input_dev->evbit[0] |= BIT_MASK(EV_REP); input_set_capability(kp->input_dev, EV_MSC, MSC_SCAN); + input_set_drvdata(kp->input_dev, kp); + error = input_register_device(kp->input_dev); if (error < 0) { dev_err(dev, "Could not register input device\n"); diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c index 67bec14e8b96..a2c6f79aa101 100644 --- a/drivers/input/keyboard/twl4030_keypad.c +++ b/drivers/input/keyboard/twl4030_keypad.c @@ -361,14 +361,6 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev) kp->irq = platform_get_irq(pdev, 0); /* setup input device */ - __set_bit(EV_KEY, input->evbit); - - /* Enable auto repeat feature of Linux input subsystem */ - if (pdata->rep) - __set_bit(EV_REP, input->evbit); - - input_set_capability(input, EV_MSC, MSC_SCAN); - input->name = "TWL4030 Keypad"; input->phys = "twl4030_keypad/input0"; input->dev.parent = &pdev->dev; @@ -378,12 +370,19 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev) input->id.product = 0x0001; input->id.version = 0x0003; - input->keycode = kp->keymap; - input->keycodesize = sizeof(kp->keymap[0]); - input->keycodemax = ARRAY_SIZE(kp->keymap); + error = matrix_keypad_build_keymap(keymap_data, NULL, + TWL4030_MAX_ROWS, + 1 << TWL4030_ROW_SHIFT, + kp->keymap, input); + if (error) { + dev_err(kp->dbg_dev, "Failed to build keymap\n"); + goto err1; + } - matrix_keypad_build_keymap(keymap_data, TWL4030_ROW_SHIFT, - input->keycode, input->keybit); + input_set_capability(input, EV_MSC, MSC_SCAN); + /* Enable auto repeat feature of Linux input subsystem */ + if (pdata->rep) + __set_bit(EV_REP, input->evbit); error = input_register_device(input); if (error) { diff --git a/drivers/input/keyboard/w90p910_keypad.c b/drivers/input/keyboard/w90p910_keypad.c index 99bbb7e775ae..085ede4d972d 100644 --- a/drivers/input/keyboard/w90p910_keypad.c +++ b/drivers/input/keyboard/w90p910_keypad.c @@ -42,7 +42,8 @@ #define KGET_RAW(n) (((n) & KEY0R) >> 3) #define KGET_COLUMN(n) ((n) & KEY0C) -#define W90P910_MAX_KEY_NUM (8 * 8) +#define W90P910_NUM_ROWS 8 +#define W90P910_NUM_COLS 8 #define W90P910_ROW_SHIFT 3 struct w90p910_keypad { @@ -51,7 +52,7 @@ struct w90p910_keypad { struct input_dev *input_dev; void __iomem *mmio_base; int irq; - unsigned short keymap[W90P910_MAX_KEY_NUM]; + unsigned short keymap[W90P910_NUM_ROWS * W90P910_NUM_COLS]; }; static void w90p910_keypad_scan_matrix(struct w90p910_keypad *keypad, @@ -190,17 +191,13 @@ static int __devinit w90p910_keypad_probe(struct platform_device *pdev) input_dev->close = w90p910_keypad_close; input_dev->dev.parent = &pdev->dev; - input_dev->keycode = keypad->keymap; - input_dev->keycodesize = sizeof(keypad->keymap[0]); - input_dev->keycodemax = ARRAY_SIZE(keypad->keymap); - - input_set_drvdata(input_dev, keypad); - - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); - input_set_capability(input_dev, EV_MSC, MSC_SCAN); - - matrix_keypad_build_keymap(keymap_data, W90P910_ROW_SHIFT, - input_dev->keycode, input_dev->keybit); + error = matrix_keypad_build_keymap(keymap_data, NULL, + W90P910_NUM_ROWS, W90P910_NUM_COLS, + keypad->keymap, input_dev); + if (error) { + dev_err(&pdev->dev, "failed to build keymap\n"); + goto failed_put_clk; + } error = request_irq(keypad->irq, w90p910_keypad_irq_handler, 0, pdev->name, keypad); @@ -209,6 +206,10 @@ static int __devinit w90p910_keypad_probe(struct platform_device *pdev) goto failed_put_clk; } + __set_bit(EV_REP, input_dev->evbit); + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + input_set_drvdata(input_dev, keypad); + /* Register the input device */ error = input_register_device(input_dev); if (error) { diff --git a/drivers/input/matrix-keymap.c b/drivers/input/matrix-keymap.c new file mode 100644 index 000000000000..de7992d55da2 --- /dev/null +++ b/drivers/input/matrix-keymap.c @@ -0,0 +1,142 @@ +/* + * Helpers for matrix keyboard bindings + * + * Copyright (C) 2012 Google, Inc + * + * Author: + * Olof Johansson + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +/** + * matrix_keypad_build_keymap - convert platform keymap into matrix keymap + * @keymap_data: keymap supplied by the platform code + * @keymap_name: name of device tree property containing keymap (if device + * tree support is enabled). + * @rows: number of rows in target keymap array + * @cols: number of cols in target keymap array + * @keymap: expanded version of keymap that is suitable for use by + * matrix keyboard driver + * @input_dev: input devices for which we are setting up the keymap + * + * This function converts platform keymap (encoded with KEY() macro) into + * an array of keycodes that is suitable for using in a standard matrix + * keyboard driver that uses row and col as indices. + */ +int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, + const char *keymap_name, + unsigned int rows, unsigned int cols, + unsigned short *keymap, + struct input_dev *input_dev) +{ + unsigned int row_shift = get_count_order(cols); + int i; + + input_dev->keycode = keymap; + input_dev->keycodesize = sizeof(*keymap); + input_dev->keycodemax = rows << row_shift; + + __set_bit(EV_KEY, input_dev->evbit); + + for (i = 0; i < keymap_data->keymap_size; i++) { + unsigned int key = keymap_data->keymap[i]; + unsigned int row = KEY_ROW(key); + unsigned int col = KEY_COL(key); + unsigned short code = KEY_VAL(key); + + if (row >= rows || col >= cols) { + dev_err(input_dev->dev.parent, + "%s: invalid keymap entry %d (row: %d, col: %d, rows: %d, cols: %d)\n", + __func__, i, row, col, rows, cols); + return -EINVAL; + } + + keymap[MATRIX_SCAN_CODE(row, col, row_shift)] = code; + __set_bit(code, input_dev->keybit); + } + __clear_bit(KEY_RESERVED, input_dev->keybit); + + return 0; +} +EXPORT_SYMBOL(matrix_keypad_build_keymap); + +#ifdef CONFIG_OF +struct matrix_keymap_data * +matrix_keyboard_of_fill_keymap(struct device_node *np, + const char *propname) +{ + struct matrix_keymap_data *kd; + u32 *keymap; + int proplen, i; + const __be32 *prop; + + if (!np) + return NULL; + + if (!propname) + propname = "linux,keymap"; + + prop = of_get_property(np, propname, &proplen); + if (!prop) + return NULL; + + if (proplen % sizeof(u32)) { + pr_warn("Malformed keymap property %s in %s\n", + propname, np->full_name); + return NULL; + } + + kd = kzalloc(sizeof(*kd), GFP_KERNEL); + if (!kd) + return NULL; + + kd->keymap = keymap = kzalloc(proplen, GFP_KERNEL); + if (!kd->keymap) { + kfree(kd); + return NULL; + } + + kd->keymap_size = proplen / sizeof(u32); + + for (i = 0; i < kd->keymap_size; i++) { + u32 tmp = be32_to_cpup(prop + i); + int key_code, row, col; + + row = (tmp >> 24) & 0xff; + col = (tmp >> 16) & 0xff; + key_code = tmp & 0xffff; + keymap[i] = KEY(row, col, key_code); + } + + return kd; +} +EXPORT_SYMBOL_GPL(matrix_keyboard_of_fill_keymap); + +void matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd) +{ + if (kd) { + kfree(kd->keymap); + kfree(kd); + } +} +EXPORT_SYMBOL_GPL(matrix_keyboard_of_free_keymap); +#endif diff --git a/drivers/input/of_keymap.c b/drivers/input/of_keymap.c deleted file mode 100644 index 061493d57682..000000000000 --- a/drivers/input/of_keymap.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Helpers for open firmware matrix keyboard bindings - * - * Copyright (C) 2012 Google, Inc - * - * Author: - * Olof Johansson - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -struct matrix_keymap_data * -matrix_keyboard_of_fill_keymap(struct device_node *np, - const char *propname) -{ - struct matrix_keymap_data *kd; - u32 *keymap; - int proplen, i; - const __be32 *prop; - - if (!np) - return NULL; - - if (!propname) - propname = "linux,keymap"; - - prop = of_get_property(np, propname, &proplen); - if (!prop) - return NULL; - - if (proplen % sizeof(u32)) { - pr_warn("Malformed keymap property %s in %s\n", - propname, np->full_name); - return NULL; - } - - kd = kzalloc(sizeof(*kd), GFP_KERNEL); - if (!kd) - return NULL; - - kd->keymap = keymap = kzalloc(proplen, GFP_KERNEL); - if (!kd->keymap) { - kfree(kd); - return NULL; - } - - kd->keymap_size = proplen / sizeof(u32); - - for (i = 0; i < kd->keymap_size; i++) { - u32 tmp = be32_to_cpup(prop + i); - int key_code, row, col; - - row = (tmp >> 24) & 0xff; - col = (tmp >> 16) & 0xff; - key_code = tmp & 0xffff; - keymap[i] = KEY(row, col, key_code); - } - - return kd; -} -EXPORT_SYMBOL_GPL(matrix_keyboard_of_fill_keymap); - -void matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd) -{ - if (kd) { - kfree(kd->keymap); - kfree(kd); - } -} -EXPORT_SYMBOL_GPL(matrix_keyboard_of_free_keymap); diff --git a/include/linux/input/matrix_keypad.h b/include/linux/input/matrix_keypad.h index 6c07ced0af81..e8fe08ebc245 100644 --- a/include/linux/input/matrix_keypad.h +++ b/include/linux/input/matrix_keypad.h @@ -75,39 +75,13 @@ struct matrix_keypad_platform_data { bool no_autorepeat; }; -/** - * matrix_keypad_build_keymap - convert platform keymap into matrix keymap - * @keymap_data: keymap supplied by the platform code - * @row_shift: number of bits to shift row value by to advance to the next - * line in the keymap - * @keymap: expanded version of keymap that is suitable for use by - * matrix keyboad driver - * @keybit: pointer to bitmap of keys supported by input device - * - * This function converts platform keymap (encoded with KEY() macro) into - * an array of keycodes that is suitable for using in a standard matrix - * keyboard driver that uses row and col as indices. - */ -static inline void -matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, - unsigned int row_shift, - unsigned short *keymap, unsigned long *keybit) -{ - int i; - - for (i = 0; i < keymap_data->keymap_size; i++) { - unsigned int key = keymap_data->keymap[i]; - unsigned int row = KEY_ROW(key); - unsigned int col = KEY_COL(key); - unsigned short code = KEY_VAL(key); - - keymap[MATRIX_SCAN_CODE(row, col, row_shift)] = code; - __set_bit(code, keybit); - } - __clear_bit(KEY_RESERVED, keybit); -} +int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, + const char *keymap_name, + unsigned int rows, unsigned int cols, + unsigned short *keymap, + struct input_dev *input_dev); -#ifdef CONFIG_INPUT_OF_MATRIX_KEYMAP +#ifdef CONFIG_OF struct matrix_keymap_data * matrix_keyboard_of_fill_keymap(struct device_node *np, const char *propname); -- cgit v1.2.3 From b45c8f35a7fec17bdd159a7815203b6137c00ab9 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 10 May 2012 22:37:15 -0700 Subject: Input: matrix-keymap - wire up device tree support When platform keymap is not supplied to matrix_keypad_build_keymap() and device tree support is enabled, try locating specified property and load keymap from it. If property name is not defined, try using "linux,keymap". Based on earlier patch by Viresh Kumar Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tegra-kbc.c | 56 +++++++----- drivers/input/matrix-keymap.c | 175 ++++++++++++++++++++---------------- include/linux/input/matrix_keypad.h | 18 ---- 3 files changed, 130 insertions(+), 119 deletions(-) (limited to 'include/linux/input') diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index 6722d376e898..4ffe64d53107 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -619,8 +619,8 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata, } #ifdef CONFIG_OF -static struct tegra_kbc_platform_data * __devinit -tegra_kbc_dt_parse_pdata(struct platform_device *pdev) +static struct tegra_kbc_platform_data * __devinit tegra_kbc_dt_parse_pdata( + struct platform_device *pdev) { struct tegra_kbc_platform_data *pdata; struct device_node *np = pdev->dev.of_node; @@ -660,10 +660,6 @@ tegra_kbc_dt_parse_pdata(struct platform_device *pdev) pdata->pin_cfg[KBC_MAX_ROW + i].type = PIN_CFG_COL; } - pdata->keymap_data = matrix_keyboard_of_fill_keymap(np, "linux,keymap"); - - /* FIXME: Add handling of linux,fn-keymap here */ - return pdata; } #else @@ -674,10 +670,36 @@ static inline struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata( } #endif +static int __devinit tegra_kbd_setup_keymap(struct tegra_kbc *kbc) +{ + const struct tegra_kbc_platform_data *pdata = kbc->pdata; + const struct matrix_keymap_data *keymap_data = pdata->keymap_data; + unsigned int keymap_rows = KBC_MAX_KEY; + int retval; + + if (keymap_data && pdata->use_fn_map) + keymap_rows *= 2; + + retval = matrix_keypad_build_keymap(keymap_data, NULL, + keymap_rows, KBC_MAX_COL, + kbc->keycode, kbc->idev); + if (retval == -ENOSYS || retval == -ENOENT) { + /* + * If there is no OF support in kernel or keymap + * property is missing, use default keymap. + */ + retval = matrix_keypad_build_keymap( + &tegra_kbc_default_keymap_data, NULL, + keymap_rows, KBC_MAX_COL, + kbc->keycode, kbc->idev); + } + + return retval; +} + static int __devinit tegra_kbc_probe(struct platform_device *pdev) { const struct tegra_kbc_platform_data *pdata = pdev->dev.platform_data; - const struct matrix_keymap_data *keymap_data; struct tegra_kbc *kbc; struct input_dev *input_dev; struct resource *res; @@ -686,7 +708,6 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) int num_rows = 0; unsigned int debounce_cnt; unsigned int scan_time_rows; - unsigned int keymap_rows; if (!pdata) pdata = tegra_kbc_dt_parse_pdata(pdev); @@ -768,17 +789,9 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) input_dev->open = tegra_kbc_open; input_dev->close = tegra_kbc_close; - keymap_rows = KBC_MAX_KEY; - if (pdata->use_fn_map) - keymap_rows *= 2; - - keymap_data = pdata->keymap_data ?: &tegra_kbc_default_keymap_data; - - err = matrix_keypad_build_keymap(keymap_data, NULL, - keymap_rows, KBC_MAX_COL, - kbc->keycode, input_dev); + err = tegra_kbd_setup_keymap(kbc); if (err) { - dev_err(&pdev->dev, "failed to build keymap\n"); + dev_err(&pdev->dev, "failed to setup keymap\n"); goto err_put_clk; } @@ -805,9 +818,6 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, kbc); device_init_wakeup(&pdev->dev, pdata->wakeup); - if (!pdev->dev.platform_data) - matrix_keyboard_of_free_keymap(pdata->keymap_data); - return 0; err_free_irq: @@ -822,10 +832,8 @@ err_free_mem: input_free_device(input_dev); kfree(kbc); err_free_pdata: - if (!pdev->dev.platform_data) { - matrix_keyboard_of_free_keymap(pdata->keymap_data); + if (!pdev->dev.platform_data) kfree(pdata); - } return err; } diff --git a/drivers/input/matrix-keymap.c b/drivers/input/matrix-keymap.c index de7992d55da2..db92c1ebfc59 100644 --- a/drivers/input/matrix-keymap.c +++ b/drivers/input/matrix-keymap.c @@ -17,15 +17,91 @@ * */ +#include #include #include #include #include #include -#include -#include #include +static bool matrix_keypad_map_key(struct input_dev *input_dev, + unsigned int rows, unsigned int cols, + unsigned int row_shift, unsigned int key) +{ + unsigned char *keymap = input_dev->keycode; + unsigned int row = KEY_ROW(key); + unsigned int col = KEY_COL(key); + unsigned short code = KEY_VAL(key); + + if (row >= rows || col >= cols) { + dev_err(input_dev->dev.parent, + "%s: invalid keymap entry 0x%x (row: %d, col: %d, rows: %d, cols: %d)\n", + __func__, key, row, col, rows, cols); + return false; + } + + keymap[MATRIX_SCAN_CODE(row, col, row_shift)] = code; + __set_bit(code, input_dev->keybit); + + return true; +} + +#ifdef CONFIG_OF +static int matrix_keypad_parse_of_keymap(const char *propname, + unsigned int rows, unsigned int cols, + struct input_dev *input_dev) +{ + struct device *dev = input_dev->dev.parent; + struct device_node *np = dev->of_node; + unsigned int row_shift = get_count_order(cols); + unsigned int max_keys = rows << row_shift; + unsigned int proplen, i, size; + const __be32 *prop; + + if (!np) + return -ENOENT; + + if (!propname) + propname = "linux,keymap"; + + prop = of_get_property(np, propname, &proplen); + if (!prop) { + dev_err(dev, "OF: %s property not defined in %s\n", + propname, np->full_name); + return -ENOENT; + } + + if (proplen % sizeof(u32)) { + dev_err(dev, "OF: Malformed keycode property %s in %s\n", + propname, np->full_name); + return -EINVAL; + } + + size = proplen / sizeof(u32); + if (size > max_keys) { + dev_err(dev, "OF: %s size overflow\n", propname); + return -EINVAL; + } + + for (i = 0; i < size; i++) { + unsigned int key = be32_to_cpup(prop + i); + + if (!matrix_keypad_map_key(input_dev, rows, cols, + row_shift, key)) + return -EINVAL; + } + + return 0; +} +#else +static int matrix_keypad_parse_of_keymap(const char *propname, + unsigned int rows, unsigned int cols, + struct input_dev *input_dev) +{ + return -ENOSYS; +} +#endif /** * matrix_keypad_build_keymap - convert platform keymap into matrix keymap @@ -41,6 +117,13 @@ * This function converts platform keymap (encoded with KEY() macro) into * an array of keycodes that is suitable for using in a standard matrix * keyboard driver that uses row and col as indices. + * + * If @keymap_data is not supplied and device tree support is enabled + * it will attempt load the keymap from property specified by @keymap_name + * argument (or "linux,keymap" if @keymap_name is %NULL). + * + * Callers are expected to set up input_dev->dev.parent before calling this + * function. */ int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, const char *keymap_name, @@ -50,6 +133,7 @@ int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, { unsigned int row_shift = get_count_order(cols); int i; + int error; input_dev->keycode = keymap; input_dev->keycodesize = sizeof(*keymap); @@ -57,86 +141,23 @@ int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, __set_bit(EV_KEY, input_dev->evbit); - for (i = 0; i < keymap_data->keymap_size; i++) { - unsigned int key = keymap_data->keymap[i]; - unsigned int row = KEY_ROW(key); - unsigned int col = KEY_COL(key); - unsigned short code = KEY_VAL(key); + if (keymap_data) { + for (i = 0; i < keymap_data->keymap_size; i++) { + unsigned int key = keymap_data->keymap[i]; - if (row >= rows || col >= cols) { - dev_err(input_dev->dev.parent, - "%s: invalid keymap entry %d (row: %d, col: %d, rows: %d, cols: %d)\n", - __func__, i, row, col, rows, cols); - return -EINVAL; + if (!matrix_keypad_map_key(input_dev, rows, cols, + row_shift, key)) + return -EINVAL; } - - keymap[MATRIX_SCAN_CODE(row, col, row_shift)] = code; - __set_bit(code, input_dev->keybit); + } else { + error = matrix_keypad_parse_of_keymap(keymap_name, rows, cols, + input_dev); + if (error) + return error; } + __clear_bit(KEY_RESERVED, input_dev->keybit); return 0; } EXPORT_SYMBOL(matrix_keypad_build_keymap); - -#ifdef CONFIG_OF -struct matrix_keymap_data * -matrix_keyboard_of_fill_keymap(struct device_node *np, - const char *propname) -{ - struct matrix_keymap_data *kd; - u32 *keymap; - int proplen, i; - const __be32 *prop; - - if (!np) - return NULL; - - if (!propname) - propname = "linux,keymap"; - - prop = of_get_property(np, propname, &proplen); - if (!prop) - return NULL; - - if (proplen % sizeof(u32)) { - pr_warn("Malformed keymap property %s in %s\n", - propname, np->full_name); - return NULL; - } - - kd = kzalloc(sizeof(*kd), GFP_KERNEL); - if (!kd) - return NULL; - - kd->keymap = keymap = kzalloc(proplen, GFP_KERNEL); - if (!kd->keymap) { - kfree(kd); - return NULL; - } - - kd->keymap_size = proplen / sizeof(u32); - - for (i = 0; i < kd->keymap_size; i++) { - u32 tmp = be32_to_cpup(prop + i); - int key_code, row, col; - - row = (tmp >> 24) & 0xff; - col = (tmp >> 16) & 0xff; - key_code = tmp & 0xffff; - keymap[i] = KEY(row, col, key_code); - } - - return kd; -} -EXPORT_SYMBOL_GPL(matrix_keyboard_of_fill_keymap); - -void matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd) -{ - if (kd) { - kfree(kd->keymap); - kfree(kd); - } -} -EXPORT_SYMBOL_GPL(matrix_keyboard_of_free_keymap); -#endif diff --git a/include/linux/input/matrix_keypad.h b/include/linux/input/matrix_keypad.h index e8fe08ebc245..5f3aa6b11bfa 100644 --- a/include/linux/input/matrix_keypad.h +++ b/include/linux/input/matrix_keypad.h @@ -81,22 +81,4 @@ int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, unsigned short *keymap, struct input_dev *input_dev); -#ifdef CONFIG_OF -struct matrix_keymap_data * -matrix_keyboard_of_fill_keymap(struct device_node *np, const char *propname); - -void matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd); -#else -static inline struct matrix_keymap_data * -matrix_keyboard_of_fill_keymap(struct device_node *np, const char *propname) -{ - return NULL; -} - -static inline void -matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd) -{ -} -#endif - #endif /* _MATRIX_KEYPAD_H */ -- cgit v1.2.3 From 43c4d13e901a8f37d9abbd410f093ebe885b5322 Mon Sep 17 00:00:00 2001 From: Simon Budig Date: Tue, 24 Jul 2012 23:29:36 -0700 Subject: Input: add driver for FT5x06 based EDT displays This is a driver for the EDT "Polytouch" family of touch controllers based on the FocalTech FT5x06 line of chips. Signed-off-by: Simon Budig Reviewed-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- Documentation/input/edt-ft5x06.txt | 54 ++ drivers/input/touchscreen/Kconfig | 13 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/edt-ft5x06.c | 898 +++++++++++++++++++++++++++++++++ include/linux/input/edt-ft5x06.h | 24 + 5 files changed, 990 insertions(+) create mode 100644 Documentation/input/edt-ft5x06.txt create mode 100644 drivers/input/touchscreen/edt-ft5x06.c create mode 100644 include/linux/input/edt-ft5x06.h (limited to 'include/linux/input') diff --git a/Documentation/input/edt-ft5x06.txt b/Documentation/input/edt-ft5x06.txt new file mode 100644 index 000000000000..2032f0b7a8fa --- /dev/null +++ b/Documentation/input/edt-ft5x06.txt @@ -0,0 +1,54 @@ +EDT ft5x06 based Polytouch devices +---------------------------------- + +The edt-ft5x06 driver is useful for the EDT "Polytouch" family of capacitive +touch screens. Note that it is *not* suitable for other devices based on the +focaltec ft5x06 devices, since they contain vendor-specific firmware. In +particular this driver is not suitable for the Nook tablet. + +It has been tested with the following devices: + * EP0350M06 + * EP0430M06 + * EP0570M06 + * EP0700M06 + +The driver allows configuration of the touch screen via a set of sysfs files: + +/sys/class/input/eventX/device/device/threshold: + allows setting the "click"-threshold in the range from 20 to 80. + +/sys/class/input/eventX/device/device/gain: + allows setting the sensitivity in the range from 0 to 31. Note that + lower values indicate higher sensitivity. + +/sys/class/input/eventX/device/device/offset: + allows setting the edge compensation in the range from 0 to 31. + +/sys/class/input/eventX/device/device/report_rate: + allows setting the report rate in the range from 3 to 14. + + +For debugging purposes the driver provides a few files in the debug +filesystem (if available in the kernel). In /sys/kernel/debug/edt_ft5x06 +you'll find the following files: + +num_x, num_y: + (readonly) contains the number of sensor fields in X- and + Y-direction. + +mode: + allows switching the sensor between "factory mode" and "operation + mode" by writing "1" or "0" to it. In factory mode (1) it is + possible to get the raw data from the sensor. Note that in factory + mode regular events don't get delivered and the options described + above are unavailable. + +raw_data: + contains num_x * num_y big endian 16 bit values describing the raw + values for each sensor field. Note that each read() call on this + files triggers a new readout. It is recommended to provide a buffer + big enough to contain num_x * num_y * 2 bytes. + +Note that reading raw_data gives a I/O error when the device is not in factory +mode. The same happens when reading/writing to the parameter files when the +device is not in regular operation mode. diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 73bd2f6b82ec..1ba232cbc09d 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -472,6 +472,19 @@ config TOUCHSCREEN_PENMOUNT To compile this driver as a module, choose M here: the module will be called penmount. +config TOUCHSCREEN_EDT_FT5X06 + tristate "EDT FocalTech FT5x06 I2C Touchscreen support" + depends on I2C + help + Say Y here if you have an EDT "Polytouch" touchscreen based + on the FocalTech FT5x06 family of controllers connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called edt-ft5x06. + config TOUCHSCREEN_MIGOR tristate "Renesas MIGO-R touchscreen" depends on SH_MIGOR && I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 5920c60f999d..178eb128d90f 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o +obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06) += edt-ft5x06.o obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c new file mode 100644 index 000000000000..9afc777a40a7 --- /dev/null +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -0,0 +1,898 @@ +/* + * Copyright (C) 2012 Simon Budig, + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * This is a driver for the EDT "Polytouch" family of touch controllers + * based on the FocalTech FT5x06 line of chips. + * + * Development of this driver has been sponsored by Glyn: + * http://www.glyn.com/Products/Displays + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_SUPPORT_POINTS 5 + +#define WORK_REGISTER_THRESHOLD 0x00 +#define WORK_REGISTER_REPORT_RATE 0x08 +#define WORK_REGISTER_GAIN 0x30 +#define WORK_REGISTER_OFFSET 0x31 +#define WORK_REGISTER_NUM_X 0x33 +#define WORK_REGISTER_NUM_Y 0x34 + +#define WORK_REGISTER_OPMODE 0x3c +#define FACTORY_REGISTER_OPMODE 0x01 + +#define TOUCH_EVENT_DOWN 0x00 +#define TOUCH_EVENT_UP 0x01 +#define TOUCH_EVENT_ON 0x02 +#define TOUCH_EVENT_RESERVED 0x03 + +#define EDT_NAME_LEN 23 +#define EDT_SWITCH_MODE_RETRIES 10 +#define EDT_SWITCH_MODE_DELAY 5 /* msec */ +#define EDT_RAW_DATA_RETRIES 100 +#define EDT_RAW_DATA_DELAY 1 /* msec */ + +struct edt_ft5x06_ts_data { + struct i2c_client *client; + struct input_dev *input; + u16 num_x; + u16 num_y; + +#if defined(CONFIG_DEBUG_FS) + struct dentry *debug_dir; + u8 *raw_buffer; + size_t raw_bufsize; +#endif + + struct mutex mutex; + bool factory_mode; + int threshold; + int gain; + int offset; + int report_rate; + + char name[EDT_NAME_LEN]; +}; + +static int edt_ft5x06_ts_readwrite(struct i2c_client *client, + u16 wr_len, u8 *wr_buf, + u16 rd_len, u8 *rd_buf) +{ + struct i2c_msg wrmsg[2]; + int i = 0; + int ret; + + if (wr_len) { + wrmsg[i].addr = client->addr; + wrmsg[i].flags = 0; + wrmsg[i].len = wr_len; + wrmsg[i].buf = wr_buf; + i++; + } + if (rd_len) { + wrmsg[i].addr = client->addr; + wrmsg[i].flags = I2C_M_RD; + wrmsg[i].len = rd_len; + wrmsg[i].buf = rd_buf; + i++; + } + + ret = i2c_transfer(client->adapter, wrmsg, i); + if (ret < 0) + return ret; + if (ret != i) + return -EIO; + + return 0; +} + +static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata, + u8 *buf, int buflen) +{ + int i; + u8 crc = 0; + + for (i = 0; i < buflen - 1; i++) + crc ^= buf[i]; + + if (crc != buf[buflen-1]) { + dev_err_ratelimited(&tsdata->client->dev, + "crc error: 0x%02x expected, got 0x%02x\n", + crc, buf[buflen-1]); + return false; + } + + return true; +} + +static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) +{ + struct edt_ft5x06_ts_data *tsdata = dev_id; + struct device *dev = &tsdata->client->dev; + u8 cmd = 0xf9; + u8 rdbuf[26]; + int i, type, x, y, id; + int error; + + memset(rdbuf, 0, sizeof(rdbuf)); + + error = edt_ft5x06_ts_readwrite(tsdata->client, + sizeof(cmd), &cmd, + sizeof(rdbuf), rdbuf); + if (error) { + dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n", + error); + goto out; + } + + if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || rdbuf[2] != 26) { + dev_err_ratelimited(dev, "Unexpected header: %02x%02x%02x!\n", + rdbuf[0], rdbuf[1], rdbuf[2]); + goto out; + } + + if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, 26)) + goto out; + + for (i = 0; i < MAX_SUPPORT_POINTS; i++) { + u8 *buf = &rdbuf[i * 4 + 5]; + bool down; + + type = buf[0] >> 6; + /* ignore Reserved events */ + if (type == TOUCH_EVENT_RESERVED) + continue; + + x = ((buf[0] << 8) | buf[1]) & 0x0fff; + y = ((buf[2] << 8) | buf[3]) & 0x0fff; + id = (buf[2] >> 4) & 0x0f; + down = (type != TOUCH_EVENT_UP); + + input_mt_slot(tsdata->input, id); + input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down); + + if (!down) + continue; + + input_report_abs(tsdata->input, ABS_MT_POSITION_X, x); + input_report_abs(tsdata->input, ABS_MT_POSITION_Y, y); + } + + input_mt_report_pointer_emulation(tsdata->input, true); + input_sync(tsdata->input); + +out: + return IRQ_HANDLED; +} + +static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata, + u8 addr, u8 value) +{ + u8 wrbuf[4]; + + wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; + wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; + wrbuf[2] = value; + wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2]; + + return edt_ft5x06_ts_readwrite(tsdata->client, 4, wrbuf, 0, NULL); +} + +static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, + u8 addr) +{ + u8 wrbuf[2], rdbuf[2]; + int error; + + wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; + wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; + wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40; + + error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, rdbuf); + if (error) + return error; + + if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) { + dev_err(&tsdata->client->dev, + "crc error: 0x%02x expected, got 0x%02x\n", + wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], rdbuf[1]); + return -EIO; + } + + return rdbuf[0]; +} + +struct edt_ft5x06_attribute { + struct device_attribute dattr; + size_t field_offset; + u8 limit_low; + u8 limit_high; + u8 addr; +}; + +#define EDT_ATTR(_field, _mode, _addr, _limit_low, _limit_high) \ + struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = { \ + .dattr = __ATTR(_field, _mode, \ + edt_ft5x06_setting_show, \ + edt_ft5x06_setting_store), \ + .field_offset = \ + offsetof(struct edt_ft5x06_ts_data, _field), \ + .limit_low = _limit_low, \ + .limit_high = _limit_high, \ + .addr = _addr, \ + } + +static ssize_t edt_ft5x06_setting_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); + struct edt_ft5x06_attribute *attr = + container_of(dattr, struct edt_ft5x06_attribute, dattr); + u8 *field = (u8 *)((char *)tsdata + attr->field_offset); + int val; + size_t count = 0; + int error = 0; + + mutex_lock(&tsdata->mutex); + + if (tsdata->factory_mode) { + error = -EIO; + goto out; + } + + val = edt_ft5x06_register_read(tsdata, attr->addr); + if (val < 0) { + error = val; + dev_err(&tsdata->client->dev, + "Failed to fetch attribute %s, error %d\n", + dattr->attr.name, error); + goto out; + } + + if (val != *field) { + dev_warn(&tsdata->client->dev, + "%s: read (%d) and stored value (%d) differ\n", + dattr->attr.name, val, *field); + *field = val; + } + + count = scnprintf(buf, PAGE_SIZE, "%d\n", val); +out: + mutex_unlock(&tsdata->mutex); + return error ?: count; +} + +static ssize_t edt_ft5x06_setting_store(struct device *dev, + struct device_attribute *dattr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); + struct edt_ft5x06_attribute *attr = + container_of(dattr, struct edt_ft5x06_attribute, dattr); + u8 *field = (u8 *)((char *)tsdata + attr->field_offset); + unsigned int val; + int error; + + mutex_lock(&tsdata->mutex); + + if (tsdata->factory_mode) { + error = -EIO; + goto out; + } + + error = kstrtouint(buf, 0, &val); + if (error) + goto out; + + if (val < attr->limit_low || val > attr->limit_high) { + error = -ERANGE; + goto out; + } + + error = edt_ft5x06_register_write(tsdata, attr->addr, val); + if (error) { + dev_err(&tsdata->client->dev, + "Failed to update attribute %s, error: %d\n", + dattr->attr.name, error); + goto out; + } + + *field = val; + +out: + mutex_unlock(&tsdata->mutex); + return error ?: count; +} + +static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, 0, 31); +static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, 0, 31); +static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, + WORK_REGISTER_THRESHOLD, 20, 80); +static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, + WORK_REGISTER_REPORT_RATE, 3, 14); + +static struct attribute *edt_ft5x06_attrs[] = { + &edt_ft5x06_attr_gain.dattr.attr, + &edt_ft5x06_attr_offset.dattr.attr, + &edt_ft5x06_attr_threshold.dattr.attr, + &edt_ft5x06_attr_report_rate.dattr.attr, + NULL +}; + +static const struct attribute_group edt_ft5x06_attr_group = { + .attrs = edt_ft5x06_attrs, +}; + +#ifdef CONFIG_DEBUG_FS +static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata) +{ + struct i2c_client *client = tsdata->client; + int retries = EDT_SWITCH_MODE_RETRIES; + int ret; + int error; + + disable_irq(client->irq); + + if (!tsdata->raw_buffer) { + tsdata->raw_bufsize = tsdata->num_x * tsdata->num_y * + sizeof(u16); + tsdata->raw_buffer = kzalloc(tsdata->raw_bufsize, GFP_KERNEL); + if (!tsdata->raw_buffer) { + error = -ENOMEM; + goto err_out; + } + } + + /* mode register is 0x3c when in the work mode */ + error = edt_ft5x06_register_write(tsdata, WORK_REGISTER_OPMODE, 0x03); + if (error) { + dev_err(&client->dev, + "failed to switch to factory mode, error %d\n", error); + goto err_out; + } + + tsdata->factory_mode = true; + do { + mdelay(EDT_SWITCH_MODE_DELAY); + /* mode register is 0x01 when in factory mode */ + ret = edt_ft5x06_register_read(tsdata, FACTORY_REGISTER_OPMODE); + if (ret == 0x03) + break; + } while (--retries > 0); + + if (retries == 0) { + dev_err(&client->dev, "not in factory mode after %dms.\n", + EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY); + error = -EIO; + goto err_out; + } + + return 0; + +err_out: + kfree(tsdata->raw_buffer); + tsdata->raw_buffer = NULL; + tsdata->factory_mode = false; + enable_irq(client->irq); + + return error; +} + +static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata) +{ + struct i2c_client *client = tsdata->client; + int retries = EDT_SWITCH_MODE_RETRIES; + int ret; + int error; + + /* mode register is 0x01 when in the factory mode */ + error = edt_ft5x06_register_write(tsdata, FACTORY_REGISTER_OPMODE, 0x1); + if (error) { + dev_err(&client->dev, + "failed to switch to work mode, error: %d\n", error); + return error; + } + + tsdata->factory_mode = false; + + do { + mdelay(EDT_SWITCH_MODE_DELAY); + /* mode register is 0x01 when in factory mode */ + ret = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OPMODE); + if (ret == 0x01) + break; + } while (--retries > 0); + + if (retries == 0) { + dev_err(&client->dev, "not in work mode after %dms.\n", + EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY); + tsdata->factory_mode = true; + return -EIO; + } + + if (tsdata->raw_buffer) + kfree(tsdata->raw_buffer); + tsdata->raw_buffer = NULL; + + /* restore parameters */ + edt_ft5x06_register_write(tsdata, WORK_REGISTER_THRESHOLD, + tsdata->threshold); + edt_ft5x06_register_write(tsdata, WORK_REGISTER_GAIN, + tsdata->gain); + edt_ft5x06_register_write(tsdata, WORK_REGISTER_OFFSET, + tsdata->offset); + edt_ft5x06_register_write(tsdata, WORK_REGISTER_REPORT_RATE, + tsdata->report_rate); + + enable_irq(client->irq); + + return 0; +} + +static int edt_ft5x06_debugfs_mode_get(void *data, u64 *mode) +{ + struct edt_ft5x06_ts_data *tsdata = data; + + *mode = tsdata->factory_mode; + + return 0; +}; + +static int edt_ft5x06_debugfs_mode_set(void *data, u64 mode) +{ + struct edt_ft5x06_ts_data *tsdata = data; + int retval = 0; + + if (mode > 1) + return -ERANGE; + + mutex_lock(&tsdata->mutex); + + if (mode != tsdata->factory_mode) { + retval = mode ? edt_ft5x06_factory_mode(tsdata) : + edt_ft5x06_work_mode(tsdata); + } + + mutex_unlock(&tsdata->mutex); + + return retval; +}; + +DEFINE_SIMPLE_ATTRIBUTE(debugfs_mode_fops, edt_ft5x06_debugfs_mode_get, + edt_ft5x06_debugfs_mode_set, "%llu\n"); + +static int edt_ft5x06_debugfs_raw_data_open(struct inode *inode, + struct file *file) +{ + file->private_data = inode->i_private; + + return 0; +} + +static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file, + char __user *buf, size_t count, loff_t *off) +{ + struct edt_ft5x06_ts_data *tsdata = file->private_data; + struct i2c_client *client = tsdata->client; + int retries = EDT_RAW_DATA_RETRIES; + int val, i, error; + size_t read = 0; + int colbytes; + char wrbuf[3]; + u8 *rdbuf; + + if (*off < 0 || *off >= tsdata->raw_bufsize) + return 0; + + mutex_lock(&tsdata->mutex); + + if (!tsdata->factory_mode || !tsdata->raw_buffer) { + error = -EIO; + goto out; + } + + error = edt_ft5x06_register_write(tsdata, 0x08, 0x01); + if (error) { + dev_dbg(&client->dev, + "failed to write 0x08 register, error %d\n", error); + goto out; + } + + do { + msleep(EDT_RAW_DATA_DELAY); + val = edt_ft5x06_register_read(tsdata, 0x08); + if (val < 1) + break; + } while (--retries > 0); + + if (val < 0) { + error = val; + dev_dbg(&client->dev, + "failed to read 0x08 register, error %d\n", error); + goto out; + } + + if (retries == 0) { + dev_dbg(&client->dev, + "timed out waiting for register to settle\n"); + error = -ETIMEDOUT; + goto out; + } + + rdbuf = tsdata->raw_buffer; + colbytes = tsdata->num_y * sizeof(u16); + + wrbuf[0] = 0xf5; + wrbuf[1] = 0x0e; + for (i = 0; i < tsdata->num_x; i++) { + wrbuf[2] = i; /* column index */ + error = edt_ft5x06_ts_readwrite(tsdata->client, + sizeof(wrbuf), wrbuf, + colbytes, rdbuf); + if (error) + goto out; + + rdbuf += colbytes; + } + + read = min_t(size_t, count, tsdata->raw_bufsize - *off); + error = copy_to_user(buf, tsdata->raw_buffer + *off, read); + if (!error) + *off += read; +out: + mutex_unlock(&tsdata->mutex); + return error ?: read; +}; + + +static const struct file_operations debugfs_raw_data_fops = { + .open = edt_ft5x06_debugfs_raw_data_open, + .read = edt_ft5x06_debugfs_raw_data_read, +}; + +static void __devinit +edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, + const char *debugfs_name) +{ + tsdata->debug_dir = debugfs_create_dir(debugfs_name, NULL); + if (!tsdata->debug_dir) + return; + + debugfs_create_u16("num_x", S_IRUSR, tsdata->debug_dir, &tsdata->num_x); + debugfs_create_u16("num_y", S_IRUSR, tsdata->debug_dir, &tsdata->num_y); + + debugfs_create_file("mode", S_IRUSR | S_IWUSR, + tsdata->debug_dir, tsdata, &debugfs_mode_fops); + debugfs_create_file("raw_data", S_IRUSR, + tsdata->debug_dir, tsdata, &debugfs_raw_data_fops); +} + +static void __devexit +edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) +{ + if (tsdata->debug_dir) + debugfs_remove_recursive(tsdata->debug_dir); +} + +#else + +static inline void +edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, + const char *debugfs_name) +{ +} + +static inline void +edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) +{ +} + +#endif /* CONFIG_DEBUGFS */ + + + +static int __devinit edt_ft5x06_ts_reset(struct i2c_client *client, + int reset_pin) +{ + int error; + + if (gpio_is_valid(reset_pin)) { + /* this pulls reset down, enabling the low active reset */ + error = gpio_request_one(reset_pin, GPIOF_OUT_INIT_LOW, + "edt-ft5x06 reset"); + if (error) { + dev_err(&client->dev, + "Failed to request GPIO %d as reset pin, error %d\n", + reset_pin, error); + return error; + } + + mdelay(50); + gpio_set_value(reset_pin, 1); + mdelay(100); + } + + return 0; +} + +static int __devinit edt_ft5x06_ts_identify(struct i2c_client *client, + char *model_name, + char *fw_version) +{ + u8 rdbuf[EDT_NAME_LEN]; + char *p; + int error; + + error = edt_ft5x06_ts_readwrite(client, 1, "\xbb", + EDT_NAME_LEN - 1, rdbuf); + if (error) + return error; + + /* remove last '$' end marker */ + rdbuf[EDT_NAME_LEN - 1] = '\0'; + if (rdbuf[EDT_NAME_LEN - 2] == '$') + rdbuf[EDT_NAME_LEN - 2] = '\0'; + + /* look for Model/Version separator */ + p = strchr(rdbuf, '*'); + if (p) + *p++ = '\0'; + + strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN); + strlcpy(fw_version, p ? p : "", EDT_NAME_LEN); + + return 0; +} + +#define EDT_ATTR_CHECKSET(name, reg) \ + if (pdata->name >= edt_ft5x06_attr_##name.limit_low && \ + pdata->name <= edt_ft5x06_attr_##name.limit_high) \ + edt_ft5x06_register_write(tsdata, reg, pdata->name) + +static void __devinit +edt_ft5x06_ts_get_defaults(struct edt_ft5x06_ts_data *tsdata, + const struct edt_ft5x06_platform_data *pdata) +{ + if (!pdata->use_parameters) + return; + + /* pick up defaults from the platform data */ + EDT_ATTR_CHECKSET(threshold, WORK_REGISTER_THRESHOLD); + EDT_ATTR_CHECKSET(gain, WORK_REGISTER_GAIN); + EDT_ATTR_CHECKSET(offset, WORK_REGISTER_OFFSET); + EDT_ATTR_CHECKSET(report_rate, WORK_REGISTER_REPORT_RATE); +} + +static void __devinit +edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata) +{ + tsdata->threshold = edt_ft5x06_register_read(tsdata, + WORK_REGISTER_THRESHOLD); + tsdata->gain = edt_ft5x06_register_read(tsdata, WORK_REGISTER_GAIN); + tsdata->offset = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OFFSET); + tsdata->report_rate = edt_ft5x06_register_read(tsdata, + WORK_REGISTER_REPORT_RATE); + tsdata->num_x = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_X); + tsdata->num_y = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_Y); +} + +static int __devinit edt_ft5x06_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct edt_ft5x06_platform_data *pdata = + client->dev.platform_data; + struct edt_ft5x06_ts_data *tsdata; + struct input_dev *input; + int error; + char fw_version[EDT_NAME_LEN]; + + dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n"); + + if (!pdata) { + dev_err(&client->dev, "no platform data?\n"); + return -EINVAL; + } + + error = edt_ft5x06_ts_reset(client, pdata->reset_pin); + if (error) + return error; + + if (gpio_is_valid(pdata->irq_pin)) { + error = gpio_request_one(pdata->irq_pin, + GPIOF_IN, "edt-ft5x06 irq"); + if (error) { + dev_err(&client->dev, + "Failed to request GPIO %d, error %d\n", + pdata->irq_pin, error); + return error; + } + } + + tsdata = kzalloc(sizeof(*tsdata), GFP_KERNEL); + input = input_allocate_device(); + if (!tsdata || !input) { + dev_err(&client->dev, "failed to allocate driver data.\n"); + error = -ENOMEM; + goto err_free_mem; + } + + mutex_init(&tsdata->mutex); + tsdata->client = client; + tsdata->input = input; + tsdata->factory_mode = false; + + error = edt_ft5x06_ts_identify(client, tsdata->name, fw_version); + if (error) { + dev_err(&client->dev, "touchscreen probe failed\n"); + goto err_free_mem; + } + + edt_ft5x06_ts_get_defaults(tsdata, pdata); + edt_ft5x06_ts_get_parameters(tsdata); + + dev_dbg(&client->dev, + "Model \"%s\", Rev. \"%s\", %dx%d sensors\n", + tsdata->name, fw_version, tsdata->num_x, tsdata->num_y); + + input->name = tsdata->name; + input->id.bustype = BUS_I2C; + input->dev.parent = &client->dev; + + __set_bit(EV_SYN, input->evbit); + __set_bit(EV_KEY, input->evbit); + __set_bit(EV_ABS, input->evbit); + __set_bit(BTN_TOUCH, input->keybit); + input_set_abs_params(input, ABS_X, 0, tsdata->num_x * 64 - 1, 0, 0); + input_set_abs_params(input, ABS_Y, 0, tsdata->num_y * 64 - 1, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_X, + 0, tsdata->num_x * 64 - 1, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, + 0, tsdata->num_y * 64 - 1, 0, 0); + error = input_mt_init_slots(input, MAX_SUPPORT_POINTS); + if (error) { + dev_err(&client->dev, "Unable to init MT slots.\n"); + goto err_free_mem; + } + + input_set_drvdata(input, tsdata); + i2c_set_clientdata(client, tsdata); + + error = request_threaded_irq(client->irq, NULL, edt_ft5x06_ts_isr, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->name, tsdata); + if (error) { + dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); + goto err_free_mem; + } + + error = sysfs_create_group(&client->dev.kobj, &edt_ft5x06_attr_group); + if (error) + goto err_free_irq; + + error = input_register_device(input); + if (error) + goto err_remove_attrs; + + edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev)); + device_init_wakeup(&client->dev, 1); + + dev_dbg(&client->dev, + "EDT FT5x06 initialized: IRQ pin %d, Reset pin %d.\n", + pdata->irq_pin, pdata->reset_pin); + + return 0; + +err_remove_attrs: + sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group); +err_free_irq: + free_irq(client->irq, tsdata); +err_free_mem: + input_free_device(input); + kfree(tsdata); + + if (gpio_is_valid(pdata->irq_pin)) + gpio_free(pdata->irq_pin); + + return error; +} + +static int __devexit edt_ft5x06_ts_remove(struct i2c_client *client) +{ + const struct edt_ft5x06_platform_data *pdata = + dev_get_platdata(&client->dev); + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); + + edt_ft5x06_ts_teardown_debugfs(tsdata); + sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group); + + free_irq(client->irq, tsdata); + input_unregister_device(tsdata->input); + + if (gpio_is_valid(pdata->irq_pin)) + gpio_free(pdata->irq_pin); + if (gpio_is_valid(pdata->reset_pin)) + gpio_free(pdata->reset_pin); + + kfree(tsdata->raw_buffer); + kfree(tsdata); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int edt_ft5x06_ts_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int edt_ft5x06_ts_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(client->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops, + edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume); + +static const struct i2c_device_id edt_ft5x06_ts_id[] = { + { "edt-ft5x06", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id); + +static struct i2c_driver edt_ft5x06_ts_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "edt_ft5x06", + .pm = &edt_ft5x06_ts_pm_ops, + }, + .id_table = edt_ft5x06_ts_id, + .probe = edt_ft5x06_ts_probe, + .remove = __devexit_p(edt_ft5x06_ts_remove), +}; + +module_i2c_driver(edt_ft5x06_ts_driver); + +MODULE_AUTHOR("Simon Budig "); +MODULE_DESCRIPTION("EDT FT5x06 I2C Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/input/edt-ft5x06.h b/include/linux/input/edt-ft5x06.h new file mode 100644 index 000000000000..8a1e0d1a0124 --- /dev/null +++ b/include/linux/input/edt-ft5x06.h @@ -0,0 +1,24 @@ +#ifndef _EDT_FT5X06_H +#define _EDT_FT5X06_H + +/* + * Copyright (c) 2012 Simon Budig, + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +struct edt_ft5x06_platform_data { + int irq_pin; + int reset_pin; + + /* startup defaults for operational parameters */ + bool use_parameters; + u8 gain; + u8 threshold; + u8 offset; + u8 report_rate; +}; + +#endif /* _EDT_FT5X06_H */ -- cgit v1.2.3