mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-30 23:54:04 +08:00
Merge branch 'next' into for-linus
This commit is contained in:
commit
49327ad2bb
@ -18,12 +18,14 @@
|
||||
#include <linux/amba/pl022.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/mfd/ab8500.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/mach/arch.h>
|
||||
|
||||
#include <plat/pincfg.h>
|
||||
#include <plat/i2c.h>
|
||||
#include <plat/ske.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/setup.h>
|
||||
@ -47,6 +49,24 @@ static pin_cfg_t mop500_pins[] = {
|
||||
GPIO11_I2C2_SCL,
|
||||
GPIO229_I2C3_SDA,
|
||||
GPIO230_I2C3_SCL,
|
||||
|
||||
/* SKE keypad */
|
||||
GPIO153_KP_I7,
|
||||
GPIO154_KP_I6,
|
||||
GPIO155_KP_I5,
|
||||
GPIO156_KP_I4,
|
||||
GPIO157_KP_O7,
|
||||
GPIO158_KP_O6,
|
||||
GPIO159_KP_O5,
|
||||
GPIO160_KP_O4,
|
||||
GPIO161_KP_I3,
|
||||
GPIO162_KP_I2,
|
||||
GPIO163_KP_I1,
|
||||
GPIO164_KP_I0,
|
||||
GPIO165_KP_O3,
|
||||
GPIO166_KP_O2,
|
||||
GPIO167_KP_O1,
|
||||
GPIO168_KP_O0,
|
||||
};
|
||||
|
||||
static void ab4500_spi_cs_control(u32 command)
|
||||
@ -134,12 +154,120 @@ static struct amba_device *amba_devs[] __initdata = {
|
||||
&u8500_ssp0_device,
|
||||
};
|
||||
|
||||
static const unsigned int ux500_keymap[] = {
|
||||
KEY(2, 5, KEY_END),
|
||||
KEY(4, 1, KEY_POWER),
|
||||
KEY(3, 5, KEY_VOLUMEDOWN),
|
||||
KEY(1, 3, KEY_3),
|
||||
KEY(5, 2, KEY_RIGHT),
|
||||
KEY(5, 0, KEY_9),
|
||||
|
||||
KEY(0, 5, KEY_MENU),
|
||||
KEY(7, 6, KEY_ENTER),
|
||||
KEY(4, 5, KEY_0),
|
||||
KEY(6, 7, KEY_2),
|
||||
KEY(3, 4, KEY_UP),
|
||||
KEY(3, 3, KEY_DOWN),
|
||||
|
||||
KEY(6, 4, KEY_SEND),
|
||||
KEY(6, 2, KEY_BACK),
|
||||
KEY(4, 2, KEY_VOLUMEUP),
|
||||
KEY(5, 5, KEY_1),
|
||||
KEY(4, 3, KEY_LEFT),
|
||||
KEY(3, 2, KEY_7),
|
||||
};
|
||||
|
||||
static const struct matrix_keymap_data ux500_keymap_data = {
|
||||
.keymap = ux500_keymap,
|
||||
.keymap_size = ARRAY_SIZE(ux500_keymap),
|
||||
};
|
||||
|
||||
/*
|
||||
* Nomadik SKE keypad
|
||||
*/
|
||||
#define ROW_PIN_I0 164
|
||||
#define ROW_PIN_I1 163
|
||||
#define ROW_PIN_I2 162
|
||||
#define ROW_PIN_I3 161
|
||||
#define ROW_PIN_I4 156
|
||||
#define ROW_PIN_I5 155
|
||||
#define ROW_PIN_I6 154
|
||||
#define ROW_PIN_I7 153
|
||||
#define COL_PIN_O0 168
|
||||
#define COL_PIN_O1 167
|
||||
#define COL_PIN_O2 166
|
||||
#define COL_PIN_O3 165
|
||||
#define COL_PIN_O4 160
|
||||
#define COL_PIN_O5 159
|
||||
#define COL_PIN_O6 158
|
||||
#define COL_PIN_O7 157
|
||||
|
||||
#define SKE_KPD_MAX_ROWS 8
|
||||
#define SKE_KPD_MAX_COLS 8
|
||||
|
||||
static int ske_kp_rows[] = {
|
||||
ROW_PIN_I0, ROW_PIN_I1, ROW_PIN_I2, ROW_PIN_I3,
|
||||
ROW_PIN_I4, ROW_PIN_I5, ROW_PIN_I6, ROW_PIN_I7,
|
||||
};
|
||||
|
||||
/*
|
||||
* ske_set_gpio_row: request and set gpio rows
|
||||
*/
|
||||
static int ske_set_gpio_row(int gpio)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = gpio_request(gpio, "ske-kp");
|
||||
if (ret < 0) {
|
||||
pr_err("ske_set_gpio_row: gpio request failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gpio_direction_output(gpio, 1);
|
||||
if (ret < 0) {
|
||||
pr_err("ske_set_gpio_row: gpio direction failed\n");
|
||||
gpio_free(gpio);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* ske_kp_init - enable the gpio configuration
|
||||
*/
|
||||
static int ske_kp_init(void)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < SKE_KPD_MAX_ROWS; i++) {
|
||||
ret = ske_set_gpio_row(ske_kp_rows[i]);
|
||||
if (ret < 0) {
|
||||
pr_err("ske_kp_init: failed init\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ske_keypad_platform_data ske_keypad_board = {
|
||||
.init = ske_kp_init,
|
||||
.keymap_data = &ux500_keymap_data,
|
||||
.no_autorepeat = true,
|
||||
.krow = SKE_KPD_MAX_ROWS, /* 8x8 matrix */
|
||||
.kcol = SKE_KPD_MAX_COLS,
|
||||
.debounce_ms = 40, /* in millsecs */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* add any platform devices here - TODO */
|
||||
static struct platform_device *platform_devs[] __initdata = {
|
||||
&u8500_i2c0_device,
|
||||
&ux500_i2c1_device,
|
||||
&ux500_i2c2_device,
|
||||
&ux500_i2c3_device,
|
||||
&ux500_ske_keypad_device,
|
||||
};
|
||||
|
||||
static void __init u8500_init_machine(void)
|
||||
@ -154,6 +282,7 @@ static void __init u8500_init_machine(void)
|
||||
ux500_i2c1_device.dev.platform_data = &u8500_i2c1_data;
|
||||
ux500_i2c2_device.dev.platform_data = &u8500_i2c2_data;
|
||||
ux500_i2c3_device.dev.platform_data = &u8500_i2c3_data;
|
||||
ux500_ske_keypad_device.dev.platform_data = &ske_keypad_board;
|
||||
|
||||
u8500_ssp0_device.dev.platform_data = &ssp0_platform_data;
|
||||
|
||||
|
@ -477,6 +477,7 @@ static struct clk_lookup u8500_common_clks[] = {
|
||||
CLK(sdi5, "sdi5", NULL),
|
||||
CLK(uart2, "uart2", NULL),
|
||||
CLK(ske, "ske", NULL),
|
||||
CLK(ske, "nmk-ske-keypad", NULL),
|
||||
CLK(sdi2, "sdi2", NULL),
|
||||
CLK(i2c0, "nmk-i2c.0", NULL),
|
||||
CLK(fsmc, "fsmc", NULL),
|
||||
|
@ -216,3 +216,23 @@ void dma40_u8500ed_fixup(void)
|
||||
dma40_resources[1].start = U8500_DMA_LCPA_BASE_ED;
|
||||
dma40_resources[1].end = U8500_DMA_LCPA_BASE_ED + 2 * SZ_1K - 1;
|
||||
}
|
||||
|
||||
struct resource keypad_resources[] = {
|
||||
[0] = {
|
||||
.start = U8500_SKE_BASE,
|
||||
.end = U8500_SKE_BASE + SZ_4K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = IRQ_DB8500_KB,
|
||||
.end = IRQ_DB8500_KB,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
struct platform_device ux500_ske_keypad_device = {
|
||||
.name = "nmk-ske-keypad",
|
||||
.id = -1,
|
||||
.num_resources = ARRAY_SIZE(keypad_resources),
|
||||
.resource = keypad_resources,
|
||||
};
|
||||
|
@ -26,6 +26,7 @@ extern struct platform_device ux500_i2c3_device;
|
||||
extern struct platform_device u8500_i2c0_device;
|
||||
extern struct platform_device u8500_i2c4_device;
|
||||
extern struct platform_device u8500_dma40_device;
|
||||
extern struct platform_device ux500_ske_keypad_device;
|
||||
|
||||
void dma40_u8500ed_fixup(void);
|
||||
|
||||
|
@ -459,82 +459,82 @@
|
||||
#define GPIO152_KP_O9 PIN_CFG(152, ALT_C)
|
||||
|
||||
#define GPIO153_GPIO PIN_CFG(153, GPIO)
|
||||
#define GPIO153_KP_I7 PIN_CFG(153, ALT_A)
|
||||
#define GPIO153_KP_I7 PIN_CFG_PULL(153, ALT_A, DOWN)
|
||||
#define GPIO153_LCD_D24 PIN_CFG(153, ALT_B)
|
||||
#define GPIO153_U2_RXD PIN_CFG(153, ALT_C)
|
||||
|
||||
#define GPIO154_GPIO PIN_CFG(154, GPIO)
|
||||
#define GPIO154_KP_I6 PIN_CFG(154, ALT_A)
|
||||
#define GPIO154_KP_I6 PIN_CFG_PULL(154, ALT_A, DOWN)
|
||||
#define GPIO154_LCD_D25 PIN_CFG(154, ALT_B)
|
||||
#define GPIO154_U2_TXD PIN_CFG(154, ALT_C)
|
||||
|
||||
#define GPIO155_GPIO PIN_CFG(155, GPIO)
|
||||
#define GPIO155_KP_I5 PIN_CFG(155, ALT_A)
|
||||
#define GPIO155_KP_I5 PIN_CFG_PULL(155, ALT_A, DOWN)
|
||||
#define GPIO155_LCD_D26 PIN_CFG(155, ALT_B)
|
||||
#define GPIO155_STMAPE_CLK PIN_CFG(155, ALT_C)
|
||||
|
||||
#define GPIO156_GPIO PIN_CFG(156, GPIO)
|
||||
#define GPIO156_KP_I4 PIN_CFG(156, ALT_A)
|
||||
#define GPIO156_KP_I4 PIN_CFG_PULL(156, ALT_A, DOWN)
|
||||
#define GPIO156_LCD_D27 PIN_CFG(156, ALT_B)
|
||||
#define GPIO156_STMAPE_DAT3 PIN_CFG(156, ALT_C)
|
||||
|
||||
#define GPIO157_GPIO PIN_CFG(157, GPIO)
|
||||
#define GPIO157_KP_O7 PIN_CFG(157, ALT_A)
|
||||
#define GPIO157_KP_O7 PIN_CFG_PULL(157, ALT_A, UP)
|
||||
#define GPIO157_LCD_D28 PIN_CFG(157, ALT_B)
|
||||
#define GPIO157_STMAPE_DAT2 PIN_CFG(157, ALT_C)
|
||||
|
||||
#define GPIO158_GPIO PIN_CFG(158, GPIO)
|
||||
#define GPIO158_KP_O6 PIN_CFG(158, ALT_A)
|
||||
#define GPIO158_KP_O6 PIN_CFG_PULL(158, ALT_A, UP)
|
||||
#define GPIO158_LCD_D29 PIN_CFG(158, ALT_B)
|
||||
#define GPIO158_STMAPE_DAT1 PIN_CFG(158, ALT_C)
|
||||
|
||||
#define GPIO159_GPIO PIN_CFG(159, GPIO)
|
||||
#define GPIO159_KP_O5 PIN_CFG(159, ALT_A)
|
||||
#define GPIO159_KP_O5 PIN_CFG_PULL(159, ALT_A, UP)
|
||||
#define GPIO159_LCD_D30 PIN_CFG(159, ALT_B)
|
||||
#define GPIO159_STMAPE_DAT0 PIN_CFG(159, ALT_C)
|
||||
|
||||
#define GPIO160_GPIO PIN_CFG(160, GPIO)
|
||||
#define GPIO160_KP_O4 PIN_CFG(160, ALT_A)
|
||||
#define GPIO160_KP_O4 PIN_CFG_PULL(160, ALT_A, UP)
|
||||
#define GPIO160_LCD_D31 PIN_CFG(160, ALT_B)
|
||||
#define GPIO160_NONE PIN_CFG(160, ALT_C)
|
||||
|
||||
#define GPIO161_GPIO PIN_CFG(161, GPIO)
|
||||
#define GPIO161_KP_I3 PIN_CFG(161, ALT_A)
|
||||
#define GPIO161_KP_I3 PIN_CFG_PULL(161, ALT_A, DOWN)
|
||||
#define GPIO161_LCD_D32 PIN_CFG(161, ALT_B)
|
||||
#define GPIO161_UARTMOD_RXD PIN_CFG(161, ALT_C)
|
||||
|
||||
#define GPIO162_GPIO PIN_CFG(162, GPIO)
|
||||
#define GPIO162_KP_I2 PIN_CFG(162, ALT_A)
|
||||
#define GPIO162_KP_I2 PIN_CFG_PULL(162, ALT_A, DOWN)
|
||||
#define GPIO162_LCD_D33 PIN_CFG(162, ALT_B)
|
||||
#define GPIO162_UARTMOD_TXD PIN_CFG(162, ALT_C)
|
||||
|
||||
#define GPIO163_GPIO PIN_CFG(163, GPIO)
|
||||
#define GPIO163_KP_I1 PIN_CFG(163, ALT_A)
|
||||
#define GPIO163_KP_I1 PIN_CFG_PULL(163, ALT_A, DOWN)
|
||||
#define GPIO163_LCD_D34 PIN_CFG(163, ALT_B)
|
||||
#define GPIO163_STMMOD_CLK PIN_CFG(163, ALT_C)
|
||||
|
||||
#define GPIO164_GPIO PIN_CFG(164, GPIO)
|
||||
#define GPIO164_KP_I0 PIN_CFG(164, ALT_A)
|
||||
#define GPIO164_KP_I0 PIN_CFG_PULL(164, ALT_A, UP)
|
||||
#define GPIO164_LCD_D35 PIN_CFG(164, ALT_B)
|
||||
#define GPIO164_STMMOD_DAT3 PIN_CFG(164, ALT_C)
|
||||
|
||||
#define GPIO165_GPIO PIN_CFG(165, GPIO)
|
||||
#define GPIO165_KP_O3 PIN_CFG(165, ALT_A)
|
||||
#define GPIO165_KP_O3 PIN_CFG_PULL(165, ALT_A, UP)
|
||||
#define GPIO165_LCD_D36 PIN_CFG(165, ALT_B)
|
||||
#define GPIO165_STMMOD_DAT2 PIN_CFG(165, ALT_C)
|
||||
|
||||
#define GPIO166_GPIO PIN_CFG(166, GPIO)
|
||||
#define GPIO166_KP_O2 PIN_CFG(166, ALT_A)
|
||||
#define GPIO166_KP_O2 PIN_CFG_PULL(166, ALT_A, UP)
|
||||
#define GPIO166_LCD_D37 PIN_CFG(166, ALT_B)
|
||||
#define GPIO166_STMMOD_DAT1 PIN_CFG(166, ALT_C)
|
||||
|
||||
#define GPIO167_GPIO PIN_CFG(167, GPIO)
|
||||
#define GPIO167_KP_O1 PIN_CFG(167, ALT_A)
|
||||
#define GPIO167_KP_O1 PIN_CFG_PULL(167, ALT_A, UP)
|
||||
#define GPIO167_LCD_D38 PIN_CFG(167, ALT_B)
|
||||
#define GPIO167_STMMOD_DAT0 PIN_CFG(167, ALT_C)
|
||||
|
||||
#define GPIO168_GPIO PIN_CFG(168, GPIO)
|
||||
#define GPIO168_KP_O0 PIN_CFG(168, ALT_A)
|
||||
#define GPIO168_KP_O0 PIN_CFG_PULL(168, ALT_A, UP)
|
||||
#define GPIO168_LCD_D39 PIN_CFG(168, ALT_B)
|
||||
#define GPIO168_NONE PIN_CFG(168, ALT_C)
|
||||
|
||||
|
50
arch/arm/plat-nomadik/include/plat/ske.h
Normal file
50
arch/arm/plat-nomadik/include/plat/ske.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* License Terms: GNU General Public License v2
|
||||
* Author: Naveen Kumar Gaddipati <naveen.gaddipati@stericsson.com>
|
||||
*
|
||||
* ux500 Scroll key and Keypad Encoder (SKE) header
|
||||
*/
|
||||
|
||||
#ifndef __SKE_H
|
||||
#define __SKE_H
|
||||
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
|
||||
/* register definitions for SKE peripheral */
|
||||
#define SKE_CR 0x00
|
||||
#define SKE_VAL0 0x04
|
||||
#define SKE_VAL1 0x08
|
||||
#define SKE_DBCR 0x0C
|
||||
#define SKE_IMSC 0x10
|
||||
#define SKE_RIS 0x14
|
||||
#define SKE_MIS 0x18
|
||||
#define SKE_ICR 0x1C
|
||||
|
||||
/*
|
||||
* Keypad module
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct keypad_platform_data - structure for platform specific data
|
||||
* @init: pointer to keypad init function
|
||||
* @exit: pointer to keypad deinitialisation function
|
||||
* @keymap_data: matrix scan code table for keycodes
|
||||
* @krow: maximum number of rows
|
||||
* @kcol: maximum number of columns
|
||||
* @debounce_ms: platform specific debounce time
|
||||
* @no_autorepeat: flag for auto repetition
|
||||
* @wakeup_enable: allow waking up the system
|
||||
*/
|
||||
struct ske_keypad_platform_data {
|
||||
int (*init)(void);
|
||||
int (*exit)(void);
|
||||
const struct matrix_keymap_data *keymap_data;
|
||||
u8 krow;
|
||||
u8 kcol;
|
||||
u8 debounce_ms;
|
||||
bool no_autorepeat;
|
||||
bool wakeup_enable;
|
||||
};
|
||||
#endif /*__SKE_KPD_H*/
|
14
arch/arm/plat-omap/include/plat/omap4-keypad.h
Normal file
14
arch/arm/plat-omap/include/plat/omap4-keypad.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef ARCH_ARM_PLAT_OMAP4_KEYPAD_H
|
||||
#define ARCH_ARM_PLAT_OMAP4_KEYPAD_H
|
||||
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
|
||||
struct omap4_keypad_platform_data {
|
||||
const struct matrix_keymap_data *keymap_data;
|
||||
|
||||
u8 rows;
|
||||
u8 cols;
|
||||
};
|
||||
|
||||
extern int omap4_keyboard_init(struct omap4_keypad_platform_data *);
|
||||
#endif
|
@ -175,8 +175,7 @@ EXPORT_SYMBOL_GPL(unregister_keyboard_notifier);
|
||||
*/
|
||||
|
||||
struct getset_keycode_data {
|
||||
unsigned int scancode;
|
||||
unsigned int keycode;
|
||||
struct input_keymap_entry ke;
|
||||
int error;
|
||||
};
|
||||
|
||||
@ -184,32 +183,50 @@ static int getkeycode_helper(struct input_handle *handle, void *data)
|
||||
{
|
||||
struct getset_keycode_data *d = data;
|
||||
|
||||
d->error = input_get_keycode(handle->dev, d->scancode, &d->keycode);
|
||||
d->error = input_get_keycode(handle->dev, &d->ke);
|
||||
|
||||
return d->error == 0; /* stop as soon as we successfully get one */
|
||||
}
|
||||
|
||||
int getkeycode(unsigned int scancode)
|
||||
{
|
||||
struct getset_keycode_data d = { scancode, 0, -ENODEV };
|
||||
struct getset_keycode_data d = {
|
||||
.ke = {
|
||||
.flags = 0,
|
||||
.len = sizeof(scancode),
|
||||
.keycode = 0,
|
||||
},
|
||||
.error = -ENODEV,
|
||||
};
|
||||
|
||||
memcpy(d.ke.scancode, &scancode, sizeof(scancode));
|
||||
|
||||
input_handler_for_each_handle(&kbd_handler, &d, getkeycode_helper);
|
||||
|
||||
return d.error ?: d.keycode;
|
||||
return d.error ?: d.ke.keycode;
|
||||
}
|
||||
|
||||
static int setkeycode_helper(struct input_handle *handle, void *data)
|
||||
{
|
||||
struct getset_keycode_data *d = data;
|
||||
|
||||
d->error = input_set_keycode(handle->dev, d->scancode, d->keycode);
|
||||
d->error = input_set_keycode(handle->dev, &d->ke);
|
||||
|
||||
return d->error == 0; /* stop as soon as we successfully set one */
|
||||
}
|
||||
|
||||
int setkeycode(unsigned int scancode, unsigned int keycode)
|
||||
{
|
||||
struct getset_keycode_data d = { scancode, keycode, -ENODEV };
|
||||
struct getset_keycode_data d = {
|
||||
.ke = {
|
||||
.flags = 0,
|
||||
.len = sizeof(scancode),
|
||||
.keycode = keycode,
|
||||
},
|
||||
.error = -ENODEV,
|
||||
};
|
||||
|
||||
memcpy(d.ke.scancode, &scancode, sizeof(scancode));
|
||||
|
||||
input_handler_for_each_handle(&kbd_handler, &d, setkeycode_helper);
|
||||
|
||||
|
@ -566,10 +566,16 @@ static const unsigned char sysrq_xlate[KEY_MAX + 1] =
|
||||
static bool sysrq_down;
|
||||
static int sysrq_alt_use;
|
||||
static int sysrq_alt;
|
||||
static DEFINE_SPINLOCK(sysrq_event_lock);
|
||||
|
||||
static bool sysrq_filter(struct input_handle *handle, unsigned int type,
|
||||
unsigned int code, int value)
|
||||
{
|
||||
bool suppress;
|
||||
|
||||
/* We are called with interrupts disabled, just take the lock */
|
||||
spin_lock(&sysrq_event_lock);
|
||||
|
||||
if (type != EV_KEY)
|
||||
goto out;
|
||||
|
||||
@ -601,7 +607,10 @@ static bool sysrq_filter(struct input_handle *handle, unsigned int type,
|
||||
}
|
||||
|
||||
out:
|
||||
return sysrq_down;
|
||||
suppress = sysrq_down;
|
||||
spin_unlock(&sysrq_event_lock);
|
||||
|
||||
return suppress;
|
||||
}
|
||||
|
||||
static int sysrq_connect(struct input_handler *handler,
|
||||
@ -652,8 +661,8 @@ static void sysrq_disconnect(struct input_handle *handle)
|
||||
}
|
||||
|
||||
/*
|
||||
* We are matching on KEY_LEFTALT insteard of KEY_SYSRQ because not all
|
||||
* keyboards have SysRq ikey predefined and so user may add it to keymap
|
||||
* We are matching on KEY_LEFTALT instead of KEY_SYSRQ because not all
|
||||
* keyboards have SysRq key predefined and so user may add it to keymap
|
||||
* later, but we expect all such keyboards to have left alt.
|
||||
*/
|
||||
static const struct input_device_id sysrq_ids[] = {
|
||||
|
@ -1766,6 +1766,11 @@ static bool hid_ignore(struct hid_device *hdev)
|
||||
hdev->product <= USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST)
|
||||
return true;
|
||||
break;
|
||||
case USB_VENDOR_ID_HANWANG:
|
||||
if (hdev->product >= USB_DEVICE_ID_HANWANG_TABLET_FIRST &&
|
||||
hdev->product <= USB_DEVICE_ID_HANWANG_TABLET_LAST)
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hdev->type == HID_TYPE_USBMOUSE &&
|
||||
|
@ -291,6 +291,10 @@
|
||||
#define USB_DEVICE_ID_GYRATION_REMOTE_2 0x0003
|
||||
#define USB_DEVICE_ID_GYRATION_REMOTE_3 0x0008
|
||||
|
||||
#define USB_VENDOR_ID_HANWANG 0x0b57
|
||||
#define USB_DEVICE_ID_HANWANG_TABLET_FIRST 0x5000
|
||||
#define USB_DEVICE_ID_HANWANG_TABLET_LAST 0x8fff
|
||||
|
||||
#define USB_VENDOR_ID_HAPP 0x078b
|
||||
#define USB_DEVICE_ID_UGCI_DRIVING 0x0010
|
||||
#define USB_DEVICE_ID_UGCI_FLYING 0x0020
|
||||
|
@ -68,39 +68,52 @@ static const struct {
|
||||
#define map_key_clear(c) hid_map_usage_clear(hidinput, usage, &bit, \
|
||||
&max, EV_KEY, (c))
|
||||
|
||||
static inline int match_scancode(unsigned int code, unsigned int scancode)
|
||||
static bool match_scancode(struct hid_usage *usage,
|
||||
unsigned int cur_idx, unsigned int scancode)
|
||||
{
|
||||
if (scancode == 0)
|
||||
return 1;
|
||||
|
||||
return (code & (HID_USAGE_PAGE | HID_USAGE)) == scancode;
|
||||
return (usage->hid & (HID_USAGE_PAGE | HID_USAGE)) == scancode;
|
||||
}
|
||||
|
||||
static inline int match_keycode(unsigned int code, unsigned int keycode)
|
||||
static bool match_keycode(struct hid_usage *usage,
|
||||
unsigned int cur_idx, unsigned int keycode)
|
||||
{
|
||||
if (keycode == 0)
|
||||
return 1;
|
||||
|
||||
return code == keycode;
|
||||
/*
|
||||
* We should exclude unmapped usages when doing lookup by keycode.
|
||||
*/
|
||||
return (usage->type == EV_KEY && usage->code == keycode);
|
||||
}
|
||||
|
||||
static bool match_index(struct hid_usage *usage,
|
||||
unsigned int cur_idx, unsigned int idx)
|
||||
{
|
||||
return cur_idx == idx;
|
||||
}
|
||||
|
||||
typedef bool (*hid_usage_cmp_t)(struct hid_usage *usage,
|
||||
unsigned int cur_idx, unsigned int val);
|
||||
|
||||
static struct hid_usage *hidinput_find_key(struct hid_device *hid,
|
||||
unsigned int scancode,
|
||||
unsigned int keycode)
|
||||
hid_usage_cmp_t match,
|
||||
unsigned int value,
|
||||
unsigned int *usage_idx)
|
||||
{
|
||||
int i, j, k;
|
||||
unsigned int i, j, k, cur_idx = 0;
|
||||
struct hid_report *report;
|
||||
struct hid_usage *usage;
|
||||
|
||||
for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) {
|
||||
list_for_each_entry(report, &hid->report_enum[k].report_list, list) {
|
||||
for (i = 0; i < report->maxfield; i++) {
|
||||
for ( j = 0; j < report->field[i]->maxusage; j++) {
|
||||
for (j = 0; j < report->field[i]->maxusage; j++) {
|
||||
usage = report->field[i]->usage + j;
|
||||
if (usage->type == EV_KEY &&
|
||||
match_scancode(usage->hid, scancode) &&
|
||||
match_keycode(usage->code, keycode))
|
||||
return usage;
|
||||
if (usage->type == EV_KEY || usage->type == 0) {
|
||||
if (match(usage, cur_idx, value)) {
|
||||
if (usage_idx)
|
||||
*usage_idx = cur_idx;
|
||||
return usage;
|
||||
}
|
||||
cur_idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -108,39 +121,68 @@ static struct hid_usage *hidinput_find_key(struct hid_device *hid,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct hid_usage *hidinput_locate_usage(struct hid_device *hid,
|
||||
const struct input_keymap_entry *ke,
|
||||
unsigned int *index)
|
||||
{
|
||||
struct hid_usage *usage;
|
||||
unsigned int scancode;
|
||||
|
||||
if (ke->flags & INPUT_KEYMAP_BY_INDEX)
|
||||
usage = hidinput_find_key(hid, match_index, ke->index, index);
|
||||
else if (input_scancode_to_scalar(ke, &scancode) == 0)
|
||||
usage = hidinput_find_key(hid, match_scancode, scancode, index);
|
||||
else
|
||||
usage = NULL;
|
||||
|
||||
return usage;
|
||||
}
|
||||
|
||||
static int hidinput_getkeycode(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int *keycode)
|
||||
struct input_keymap_entry *ke)
|
||||
{
|
||||
struct hid_device *hid = input_get_drvdata(dev);
|
||||
struct hid_usage *usage;
|
||||
unsigned int scancode, index;
|
||||
|
||||
usage = hidinput_find_key(hid, scancode, 0);
|
||||
usage = hidinput_locate_usage(hid, ke, &index);
|
||||
if (usage) {
|
||||
*keycode = usage->code;
|
||||
ke->keycode = usage->type == EV_KEY ?
|
||||
usage->code : KEY_RESERVED;
|
||||
ke->index = index;
|
||||
scancode = usage->hid & (HID_USAGE_PAGE | HID_USAGE);
|
||||
ke->len = sizeof(scancode);
|
||||
memcpy(ke->scancode, &scancode, sizeof(scancode));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int hidinput_setkeycode(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int keycode)
|
||||
const struct input_keymap_entry *ke,
|
||||
unsigned int *old_keycode)
|
||||
{
|
||||
struct hid_device *hid = input_get_drvdata(dev);
|
||||
struct hid_usage *usage;
|
||||
int old_keycode;
|
||||
|
||||
usage = hidinput_find_key(hid, scancode, 0);
|
||||
usage = hidinput_locate_usage(hid, ke, NULL);
|
||||
if (usage) {
|
||||
old_keycode = usage->code;
|
||||
usage->code = keycode;
|
||||
*old_keycode = usage->type == EV_KEY ?
|
||||
usage->code : KEY_RESERVED;
|
||||
usage->code = ke->keycode;
|
||||
|
||||
clear_bit(old_keycode, dev->keybit);
|
||||
clear_bit(*old_keycode, dev->keybit);
|
||||
set_bit(usage->code, dev->keybit);
|
||||
dbg_hid(KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", keycode, scancode);
|
||||
/* Set the keybit for the old keycode if the old keycode is used
|
||||
* by another key */
|
||||
if (hidinput_find_key (hid, 0, old_keycode))
|
||||
set_bit(old_keycode, dev->keybit);
|
||||
dbg_hid(KERN_DEBUG "Assigned keycode %d to HID usage code %x\n",
|
||||
usage->code, usage->hid);
|
||||
|
||||
/*
|
||||
* Set the keybit for the old keycode if the old keycode is used
|
||||
* by another key
|
||||
*/
|
||||
if (hidinput_find_key(hid, match_keycode, *old_keycode, NULL))
|
||||
set_bit(*old_keycode, dev->keybit);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -748,8 +790,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
|
||||
hid->ll_driver->hidinput_input_event;
|
||||
input_dev->open = hidinput_open;
|
||||
input_dev->close = hidinput_close;
|
||||
input_dev->setkeycode = hidinput_setkeycode;
|
||||
input_dev->getkeycode = hidinput_getkeycode;
|
||||
input_dev->setkeycode_new = hidinput_setkeycode;
|
||||
input_dev->getkeycode_new = hidinput_getkeycode;
|
||||
|
||||
input_dev->name = hid->name;
|
||||
input_dev->phys = hid->phys;
|
||||
|
@ -534,6 +534,80 @@ static int handle_eviocgbit(struct input_dev *dev,
|
||||
}
|
||||
#undef OLD_KEY_MAX
|
||||
|
||||
static int evdev_handle_get_keycode(struct input_dev *dev,
|
||||
void __user *p, size_t size)
|
||||
{
|
||||
struct input_keymap_entry ke;
|
||||
int error;
|
||||
|
||||
memset(&ke, 0, sizeof(ke));
|
||||
|
||||
if (size == sizeof(unsigned int[2])) {
|
||||
/* legacy case */
|
||||
int __user *ip = (int __user *)p;
|
||||
|
||||
if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
|
||||
return -EFAULT;
|
||||
|
||||
ke.len = sizeof(unsigned int);
|
||||
ke.flags = 0;
|
||||
|
||||
error = input_get_keycode(dev, &ke);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (put_user(ke.keycode, ip + 1))
|
||||
return -EFAULT;
|
||||
|
||||
} else {
|
||||
size = min(size, sizeof(ke));
|
||||
|
||||
if (copy_from_user(&ke, p, size))
|
||||
return -EFAULT;
|
||||
|
||||
error = input_get_keycode(dev, &ke);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (copy_to_user(p, &ke, size))
|
||||
return -EFAULT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int evdev_handle_set_keycode(struct input_dev *dev,
|
||||
void __user *p, size_t size)
|
||||
{
|
||||
struct input_keymap_entry ke;
|
||||
|
||||
memset(&ke, 0, sizeof(ke));
|
||||
|
||||
if (size == sizeof(unsigned int[2])) {
|
||||
/* legacy case */
|
||||
int __user *ip = (int __user *)p;
|
||||
|
||||
if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
|
||||
return -EFAULT;
|
||||
|
||||
if (get_user(ke.keycode, ip + 1))
|
||||
return -EFAULT;
|
||||
|
||||
ke.len = sizeof(unsigned int);
|
||||
ke.flags = 0;
|
||||
|
||||
} else {
|
||||
size = min(size, sizeof(ke));
|
||||
|
||||
if (copy_from_user(&ke, p, size))
|
||||
return -EFAULT;
|
||||
|
||||
if (ke.len > sizeof(ke.scancode))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return input_set_keycode(dev, &ke);
|
||||
}
|
||||
|
||||
static long evdev_do_ioctl(struct file *file, unsigned int cmd,
|
||||
void __user *p, int compat_mode)
|
||||
{
|
||||
@ -580,25 +654,6 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
|
||||
|
||||
return 0;
|
||||
|
||||
case EVIOCGKEYCODE:
|
||||
if (get_user(t, ip))
|
||||
return -EFAULT;
|
||||
|
||||
error = input_get_keycode(dev, t, &v);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (put_user(v, ip + 1))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
|
||||
case EVIOCSKEYCODE:
|
||||
if (get_user(t, ip) || get_user(v, ip + 1))
|
||||
return -EFAULT;
|
||||
|
||||
return input_set_keycode(dev, t, v);
|
||||
|
||||
case EVIOCRMFF:
|
||||
return input_ff_erase(dev, (int)(unsigned long) p, file);
|
||||
|
||||
@ -620,7 +675,6 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
|
||||
|
||||
/* Now check variable-length commands */
|
||||
#define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
|
||||
|
||||
switch (EVIOC_MASK_SIZE(cmd)) {
|
||||
|
||||
case EVIOCGKEY(0):
|
||||
@ -654,6 +708,12 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
|
||||
return -EFAULT;
|
||||
|
||||
return error;
|
||||
|
||||
case EVIOC_MASK_SIZE(EVIOCGKEYCODE):
|
||||
return evdev_handle_get_keycode(dev, p, size);
|
||||
|
||||
case EVIOC_MASK_SIZE(EVIOCSKEYCODE):
|
||||
return evdev_handle_set_keycode(dev, p, size);
|
||||
}
|
||||
|
||||
/* Multi-number variable-length handlers */
|
||||
|
@ -59,44 +59,52 @@ MODULE_DEVICE_TABLE(pci, emu_tbl);
|
||||
|
||||
static int __devinit emu_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
{
|
||||
int ioport, iolen;
|
||||
struct emu *emu;
|
||||
struct gameport *port;
|
||||
|
||||
if (pci_enable_device(pdev))
|
||||
return -EBUSY;
|
||||
|
||||
ioport = pci_resource_start(pdev, 0);
|
||||
iolen = pci_resource_len(pdev, 0);
|
||||
|
||||
if (!request_region(ioport, iolen, "emu10k1-gp"))
|
||||
return -EBUSY;
|
||||
int error;
|
||||
|
||||
emu = kzalloc(sizeof(struct emu), GFP_KERNEL);
|
||||
port = gameport_allocate_port();
|
||||
if (!emu || !port) {
|
||||
printk(KERN_ERR "emu10k1-gp: Memory allocation failed\n");
|
||||
release_region(ioport, iolen);
|
||||
kfree(emu);
|
||||
gameport_free_port(port);
|
||||
return -ENOMEM;
|
||||
error = -ENOMEM;
|
||||
goto err_out_free;
|
||||
}
|
||||
|
||||
emu->io = ioport;
|
||||
emu->size = iolen;
|
||||
error = pci_enable_device(pdev);
|
||||
if (error)
|
||||
goto err_out_free;
|
||||
|
||||
emu->io = pci_resource_start(pdev, 0);
|
||||
emu->size = pci_resource_len(pdev, 0);
|
||||
|
||||
emu->dev = pdev;
|
||||
emu->gameport = port;
|
||||
|
||||
gameport_set_name(port, "EMU10K1");
|
||||
gameport_set_phys(port, "pci%s/gameport0", pci_name(pdev));
|
||||
port->dev.parent = &pdev->dev;
|
||||
port->io = ioport;
|
||||
port->io = emu->io;
|
||||
|
||||
if (!request_region(emu->io, emu->size, "emu10k1-gp")) {
|
||||
printk(KERN_ERR "emu10k1-gp: unable to grab region 0x%x-0x%x\n",
|
||||
emu->io, emu->io + emu->size - 1);
|
||||
error = -EBUSY;
|
||||
goto err_out_disable_dev;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, emu);
|
||||
|
||||
gameport_register_port(port);
|
||||
|
||||
return 0;
|
||||
|
||||
err_out_disable_dev:
|
||||
pci_disable_device(pdev);
|
||||
err_out_free:
|
||||
gameport_free_port(port);
|
||||
kfree(emu);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void __devexit emu_remove(struct pci_dev *pdev)
|
||||
@ -106,6 +114,8 @@ static void __devexit emu_remove(struct pci_dev *pdev)
|
||||
gameport_unregister_port(emu->gameport);
|
||||
release_region(emu->io, emu->size);
|
||||
kfree(emu);
|
||||
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
static struct pci_driver emu_driver = {
|
||||
|
@ -133,11 +133,11 @@ static void __devexit fm801_gp_remove(struct pci_dev *pci)
|
||||
{
|
||||
struct fm801_gp *gp = pci_get_drvdata(pci);
|
||||
|
||||
if (gp) {
|
||||
gameport_unregister_port(gp->gameport);
|
||||
release_resource(gp->res_port);
|
||||
kfree(gp);
|
||||
}
|
||||
gameport_unregister_port(gp->gameport);
|
||||
release_resource(gp->res_port);
|
||||
kfree(gp);
|
||||
|
||||
pci_disable_device(pci);
|
||||
}
|
||||
|
||||
static const struct pci_device_id fm801_gp_id_table[] = {
|
||||
|
@ -171,7 +171,7 @@ static int input_handle_abs_event(struct input_dev *dev,
|
||||
if (code == ABS_MT_SLOT) {
|
||||
/*
|
||||
* "Stage" the event; we'll flush it later, when we
|
||||
* get actiual touch data.
|
||||
* get actual touch data.
|
||||
*/
|
||||
if (*pval >= 0 && *pval < dev->mtsize)
|
||||
dev->slot = *pval;
|
||||
@ -188,7 +188,7 @@ static int input_handle_abs_event(struct input_dev *dev,
|
||||
pold = &mtslot->abs[code - ABS_MT_FIRST];
|
||||
} else {
|
||||
/*
|
||||
* Bypass filtering for multitouch events when
|
||||
* Bypass filtering for multi-touch events when
|
||||
* not employing slots.
|
||||
*/
|
||||
pold = NULL;
|
||||
@ -634,78 +634,141 @@ static void input_disconnect_device(struct input_dev *dev)
|
||||
spin_unlock_irq(&dev->event_lock);
|
||||
}
|
||||
|
||||
static int input_fetch_keycode(struct input_dev *dev, int scancode)
|
||||
/**
|
||||
* input_scancode_to_scalar() - converts scancode in &struct input_keymap_entry
|
||||
* @ke: keymap entry containing scancode to be converted.
|
||||
* @scancode: pointer to the location where converted scancode should
|
||||
* be stored.
|
||||
*
|
||||
* This function is used to convert scancode stored in &struct keymap_entry
|
||||
* into scalar form understood by legacy keymap handling methods. These
|
||||
* methods expect scancodes to be represented as 'unsigned int'.
|
||||
*/
|
||||
int input_scancode_to_scalar(const struct input_keymap_entry *ke,
|
||||
unsigned int *scancode)
|
||||
{
|
||||
switch (ke->len) {
|
||||
case 1:
|
||||
*scancode = *((u8 *)ke->scancode);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
*scancode = *((u16 *)ke->scancode);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
*scancode = *((u32 *)ke->scancode);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(input_scancode_to_scalar);
|
||||
|
||||
/*
|
||||
* Those routines handle the default case where no [gs]etkeycode() is
|
||||
* defined. In this case, an array indexed by the scancode is used.
|
||||
*/
|
||||
|
||||
static unsigned int input_fetch_keycode(struct input_dev *dev,
|
||||
unsigned int index)
|
||||
{
|
||||
switch (dev->keycodesize) {
|
||||
case 1:
|
||||
return ((u8 *)dev->keycode)[scancode];
|
||||
case 1:
|
||||
return ((u8 *)dev->keycode)[index];
|
||||
|
||||
case 2:
|
||||
return ((u16 *)dev->keycode)[scancode];
|
||||
case 2:
|
||||
return ((u16 *)dev->keycode)[index];
|
||||
|
||||
default:
|
||||
return ((u32 *)dev->keycode)[scancode];
|
||||
default:
|
||||
return ((u32 *)dev->keycode)[index];
|
||||
}
|
||||
}
|
||||
|
||||
static int input_default_getkeycode(struct input_dev *dev,
|
||||
unsigned int scancode,
|
||||
unsigned int *keycode)
|
||||
struct input_keymap_entry *ke)
|
||||
{
|
||||
unsigned int index;
|
||||
int error;
|
||||
|
||||
if (!dev->keycodesize)
|
||||
return -EINVAL;
|
||||
|
||||
if (scancode >= dev->keycodemax)
|
||||
if (ke->flags & INPUT_KEYMAP_BY_INDEX)
|
||||
index = ke->index;
|
||||
else {
|
||||
error = input_scancode_to_scalar(ke, &index);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
if (index >= dev->keycodemax)
|
||||
return -EINVAL;
|
||||
|
||||
*keycode = input_fetch_keycode(dev, scancode);
|
||||
ke->keycode = input_fetch_keycode(dev, index);
|
||||
ke->index = index;
|
||||
ke->len = sizeof(index);
|
||||
memcpy(ke->scancode, &index, sizeof(index));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int input_default_setkeycode(struct input_dev *dev,
|
||||
unsigned int scancode,
|
||||
unsigned int keycode)
|
||||
const struct input_keymap_entry *ke,
|
||||
unsigned int *old_keycode)
|
||||
{
|
||||
int old_keycode;
|
||||
unsigned int index;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
if (scancode >= dev->keycodemax)
|
||||
return -EINVAL;
|
||||
|
||||
if (!dev->keycodesize)
|
||||
return -EINVAL;
|
||||
|
||||
if (dev->keycodesize < sizeof(keycode) && (keycode >> (dev->keycodesize * 8)))
|
||||
if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
|
||||
index = ke->index;
|
||||
} else {
|
||||
error = input_scancode_to_scalar(ke, &index);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
if (index >= dev->keycodemax)
|
||||
return -EINVAL;
|
||||
|
||||
if (dev->keycodesize < sizeof(dev->keycode) &&
|
||||
(ke->keycode >> (dev->keycodesize * 8)))
|
||||
return -EINVAL;
|
||||
|
||||
switch (dev->keycodesize) {
|
||||
case 1: {
|
||||
u8 *k = (u8 *)dev->keycode;
|
||||
old_keycode = k[scancode];
|
||||
k[scancode] = keycode;
|
||||
*old_keycode = k[index];
|
||||
k[index] = ke->keycode;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
u16 *k = (u16 *)dev->keycode;
|
||||
old_keycode = k[scancode];
|
||||
k[scancode] = keycode;
|
||||
*old_keycode = k[index];
|
||||
k[index] = ke->keycode;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
u32 *k = (u32 *)dev->keycode;
|
||||
old_keycode = k[scancode];
|
||||
k[scancode] = keycode;
|
||||
*old_keycode = k[index];
|
||||
k[index] = ke->keycode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
__clear_bit(old_keycode, dev->keybit);
|
||||
__set_bit(keycode, dev->keybit);
|
||||
__clear_bit(*old_keycode, dev->keybit);
|
||||
__set_bit(ke->keycode, dev->keybit);
|
||||
|
||||
for (i = 0; i < dev->keycodemax; i++) {
|
||||
if (input_fetch_keycode(dev, i) == old_keycode) {
|
||||
__set_bit(old_keycode, dev->keybit);
|
||||
if (input_fetch_keycode(dev, i) == *old_keycode) {
|
||||
__set_bit(*old_keycode, dev->keybit);
|
||||
break; /* Setting the bit twice is useless, so break */
|
||||
}
|
||||
}
|
||||
@ -716,53 +779,86 @@ static int input_default_setkeycode(struct input_dev *dev,
|
||||
/**
|
||||
* input_get_keycode - retrieve keycode currently mapped to a given scancode
|
||||
* @dev: input device which keymap is being queried
|
||||
* @scancode: scancode (or its equivalent for device in question) for which
|
||||
* keycode is needed
|
||||
* @keycode: result
|
||||
* @ke: keymap entry
|
||||
*
|
||||
* This function should be called by anyone interested in retrieving current
|
||||
* keymap. Presently keyboard and evdev handlers use it.
|
||||
* keymap. Presently evdev handlers use it.
|
||||
*/
|
||||
int input_get_keycode(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int *keycode)
|
||||
int input_get_keycode(struct input_dev *dev, struct input_keymap_entry *ke)
|
||||
{
|
||||
unsigned long flags;
|
||||
int retval;
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
retval = dev->getkeycode(dev, scancode, keycode);
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
|
||||
if (dev->getkeycode) {
|
||||
/*
|
||||
* Support for legacy drivers, that don't implement the new
|
||||
* ioctls
|
||||
*/
|
||||
u32 scancode = ke->index;
|
||||
|
||||
memcpy(ke->scancode, &scancode, sizeof(scancode));
|
||||
ke->len = sizeof(scancode);
|
||||
retval = dev->getkeycode(dev, scancode, &ke->keycode);
|
||||
} else {
|
||||
retval = dev->getkeycode_new(dev, ke);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL(input_get_keycode);
|
||||
|
||||
/**
|
||||
* input_get_keycode - assign new keycode to a given scancode
|
||||
* input_set_keycode - attribute a keycode to a given scancode
|
||||
* @dev: input device which keymap is being updated
|
||||
* @scancode: scancode (or its equivalent for device in question)
|
||||
* @keycode: new keycode to be assigned to the scancode
|
||||
* @ke: new keymap entry
|
||||
*
|
||||
* This function should be called by anyone needing to update current
|
||||
* keymap. Presently keyboard and evdev handlers use it.
|
||||
*/
|
||||
int input_set_keycode(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int keycode)
|
||||
const struct input_keymap_entry *ke)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int old_keycode;
|
||||
int retval;
|
||||
|
||||
if (keycode > KEY_MAX)
|
||||
if (ke->keycode > KEY_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
|
||||
retval = dev->getkeycode(dev, scancode, &old_keycode);
|
||||
if (retval)
|
||||
goto out;
|
||||
if (dev->setkeycode) {
|
||||
/*
|
||||
* Support for legacy drivers, that don't implement the new
|
||||
* ioctls
|
||||
*/
|
||||
unsigned int scancode;
|
||||
|
||||
retval = input_scancode_to_scalar(ke, &scancode);
|
||||
if (retval)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* We need to know the old scancode, in order to generate a
|
||||
* keyup effect, if the set operation happens successfully
|
||||
*/
|
||||
if (!dev->getkeycode) {
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
retval = dev->getkeycode(dev, scancode, &old_keycode);
|
||||
if (retval)
|
||||
goto out;
|
||||
|
||||
retval = dev->setkeycode(dev, scancode, ke->keycode);
|
||||
} else {
|
||||
retval = dev->setkeycode_new(dev, ke, &old_keycode);
|
||||
}
|
||||
|
||||
retval = dev->setkeycode(dev, scancode, keycode);
|
||||
if (retval)
|
||||
goto out;
|
||||
|
||||
@ -1601,7 +1697,7 @@ EXPORT_SYMBOL(input_free_device);
|
||||
*
|
||||
* This function allocates all necessary memory for MT slot handling in the
|
||||
* input device, and adds ABS_MT_SLOT to the device capabilities. All slots
|
||||
* are initially marked as unused iby setting ABS_MT_TRACKING_ID to -1.
|
||||
* are initially marked as unused by setting ABS_MT_TRACKING_ID to -1.
|
||||
*/
|
||||
int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots)
|
||||
{
|
||||
@ -1759,11 +1855,11 @@ int input_register_device(struct input_dev *dev)
|
||||
dev->rep[REP_PERIOD] = 33;
|
||||
}
|
||||
|
||||
if (!dev->getkeycode)
|
||||
dev->getkeycode = input_default_getkeycode;
|
||||
if (!dev->getkeycode && !dev->getkeycode_new)
|
||||
dev->getkeycode_new = input_default_getkeycode;
|
||||
|
||||
if (!dev->setkeycode)
|
||||
dev->setkeycode = input_default_setkeycode;
|
||||
if (!dev->setkeycode && !dev->setkeycode_new)
|
||||
dev->setkeycode_new = input_default_setkeycode;
|
||||
|
||||
dev_set_name(&dev->dev, "input%ld",
|
||||
(unsigned long) atomic_inc_return(&input_no) - 1);
|
||||
|
@ -327,6 +327,16 @@ config KEYBOARD_NEWTON
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called newtonkbd.
|
||||
|
||||
config KEYBOARD_NOMADIK
|
||||
tristate "ST-Ericsson Nomadik SKE keyboard"
|
||||
depends on PLAT_NOMADIK
|
||||
help
|
||||
Say Y here if you want to use a keypad provided on the SKE controller
|
||||
used on the Ux500 and Nomadik platforms
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called nmk-ske-keypad.
|
||||
|
||||
config KEYBOARD_OPENCORES
|
||||
tristate "OpenCores Keyboard Controller"
|
||||
help
|
||||
@ -424,6 +434,15 @@ config KEYBOARD_OMAP
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called omap-keypad.
|
||||
|
||||
config KEYBOARD_OMAP4
|
||||
tristate "TI OMAP4 keypad support"
|
||||
depends on ARCH_OMAP4
|
||||
help
|
||||
Say Y here if you want to use the OMAP4 keypad.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called omap4-keypad.
|
||||
|
||||
config KEYBOARD_TWL4030
|
||||
tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
|
||||
depends on TWL4030_CORE
|
||||
|
@ -28,7 +28,9 @@ 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_NOMADIK) += nomadik-ske-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
|
||||
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
|
||||
|
@ -660,7 +660,7 @@ static const struct dev_pm_ops adp5588_dev_pm_ops = {
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id adp5588_id[] = {
|
||||
{ KBUILD_MODNAME, 0 },
|
||||
{ "adp5588-keys", 0 },
|
||||
{ "adp5587-keys", 0 },
|
||||
{ }
|
||||
};
|
||||
|
@ -570,6 +570,8 @@ static struct serio_device_id hil_dev_ids[] = {
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(serio, hil_dev_ids);
|
||||
|
||||
static struct serio_driver hil_serio_drv = {
|
||||
.driver = {
|
||||
.name = "hil_dev",
|
||||
|
408
drivers/input/keyboard/nomadik-ske-keypad.c
Normal file
408
drivers/input/keyboard/nomadik-ske-keypad.c
Normal file
@ -0,0 +1,408 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
|
||||
* Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
|
||||
*
|
||||
* License terms:GNU General Public License (GPL) version 2
|
||||
*
|
||||
* Keypad controller driver for the SKE (Scroll Key Encoder) module used in
|
||||
* the Nomadik 8815 and Ux500 platforms.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <plat/ske.h>
|
||||
|
||||
/* SKE_CR bits */
|
||||
#define SKE_KPMLT (0x1 << 6)
|
||||
#define SKE_KPCN (0x7 << 3)
|
||||
#define SKE_KPASEN (0x1 << 2)
|
||||
#define SKE_KPASON (0x1 << 7)
|
||||
|
||||
/* SKE_IMSC bits */
|
||||
#define SKE_KPIMA (0x1 << 2)
|
||||
|
||||
/* SKE_ICR bits */
|
||||
#define SKE_KPICS (0x1 << 3)
|
||||
#define SKE_KPICA (0x1 << 2)
|
||||
|
||||
/* SKE_RIS bits */
|
||||
#define SKE_KPRISA (0x1 << 2)
|
||||
|
||||
#define SKE_KEYPAD_ROW_SHIFT 3
|
||||
#define SKE_KPD_KEYMAP_SIZE (8 * 8)
|
||||
|
||||
/* keypad auto scan registers */
|
||||
#define SKE_ASR0 0x20
|
||||
#define SKE_ASR1 0x24
|
||||
#define SKE_ASR2 0x28
|
||||
#define SKE_ASR3 0x2C
|
||||
|
||||
#define SKE_NUM_ASRX_REGISTERS (4)
|
||||
|
||||
/**
|
||||
* struct ske_keypad - data structure used by keypad driver
|
||||
* @irq: irq no
|
||||
* @reg_base: ske regsiters base address
|
||||
* @input: pointer to input device object
|
||||
* @board: keypad platform device
|
||||
* @keymap: matrix scan code table for keycodes
|
||||
* @clk: clock structure pointer
|
||||
*/
|
||||
struct ske_keypad {
|
||||
int irq;
|
||||
void __iomem *reg_base;
|
||||
struct input_dev *input;
|
||||
const struct ske_keypad_platform_data *board;
|
||||
unsigned short keymap[SKE_KPD_KEYMAP_SIZE];
|
||||
struct clk *clk;
|
||||
spinlock_t ske_keypad_lock;
|
||||
};
|
||||
|
||||
static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr,
|
||||
u8 mask, u8 data)
|
||||
{
|
||||
u32 ret;
|
||||
|
||||
spin_lock(&keypad->ske_keypad_lock);
|
||||
|
||||
ret = readl(keypad->reg_base + addr);
|
||||
ret &= ~mask;
|
||||
ret |= data;
|
||||
writel(ret, keypad->reg_base + addr);
|
||||
|
||||
spin_unlock(&keypad->ske_keypad_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* ske_keypad_chip_init: init keypad controller configuration
|
||||
*
|
||||
* Enable Multi key press detection, auto scan mode
|
||||
*/
|
||||
static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad)
|
||||
{
|
||||
u32 value;
|
||||
int timeout = 50;
|
||||
|
||||
/* check SKE_RIS to be 0 */
|
||||
while ((readl(keypad->reg_base + SKE_RIS) != 0x00000000) && timeout--)
|
||||
cpu_relax();
|
||||
|
||||
if (!timeout)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* set debounce value
|
||||
* keypad dbounce is configured in DBCR[15:8]
|
||||
* dbounce value in steps of 32/32.768 ms
|
||||
*/
|
||||
spin_lock(&keypad->ske_keypad_lock);
|
||||
value = readl(keypad->reg_base + SKE_DBCR);
|
||||
value = value & 0xff;
|
||||
value |= ((keypad->board->debounce_ms * 32000)/32768) << 8;
|
||||
writel(value, keypad->reg_base + SKE_DBCR);
|
||||
spin_unlock(&keypad->ske_keypad_lock);
|
||||
|
||||
/* enable multi key detection */
|
||||
ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPMLT);
|
||||
|
||||
/*
|
||||
* set up the number of columns
|
||||
* KPCN[5:3] defines no. of keypad columns to be auto scanned
|
||||
*/
|
||||
value = (keypad->board->kcol - 1) << 3;
|
||||
ske_keypad_set_bits(keypad, SKE_CR, SKE_KPCN, value);
|
||||
|
||||
/* clear keypad interrupt for auto(and pending SW) scans */
|
||||
ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA | SKE_KPICS);
|
||||
|
||||
/* un-mask keypad interrupts */
|
||||
ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
|
||||
|
||||
/* enable automatic scan */
|
||||
ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPASEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ske_keypad_read_data(struct ske_keypad *keypad)
|
||||
{
|
||||
struct input_dev *input = keypad->input;
|
||||
u16 status;
|
||||
int col = 0, row = 0, code;
|
||||
int ske_asr, ske_ris, key_pressed, i;
|
||||
|
||||
/*
|
||||
* Read the auto scan registers
|
||||
*
|
||||
* Each SKE_ASRx (x=0 to x=3) contains two row values.
|
||||
* lower byte contains row value for column 2*x,
|
||||
* upper byte contains row value for column 2*x + 1
|
||||
*/
|
||||
for (i = 0; i < SKE_NUM_ASRX_REGISTERS; i++) {
|
||||
ske_asr = readl(keypad->reg_base + SKE_ASR0 + (4 * i));
|
||||
if (!ske_asr)
|
||||
continue;
|
||||
|
||||
/* now that ASRx is zero, find out the column x and row y*/
|
||||
if (ske_asr & 0xff) {
|
||||
col = i * 2;
|
||||
status = ske_asr & 0xff;
|
||||
} else {
|
||||
col = (i * 2) + 1;
|
||||
status = (ske_asr & 0xff00) >> 8;
|
||||
}
|
||||
|
||||
/* find out the row */
|
||||
row = __ffs(status);
|
||||
|
||||
code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT);
|
||||
ske_ris = readl(keypad->reg_base + SKE_RIS);
|
||||
key_pressed = ske_ris & SKE_KPRISA;
|
||||
|
||||
input_event(input, EV_MSC, MSC_SCAN, code);
|
||||
input_report_key(input, keypad->keymap[code], key_pressed);
|
||||
input_sync(input);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t ske_keypad_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct ske_keypad *keypad = dev_id;
|
||||
int retries = 20;
|
||||
|
||||
/* disable auto scan interrupt; mask the interrupt generated */
|
||||
ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
|
||||
ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA);
|
||||
|
||||
while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --retries)
|
||||
msleep(5);
|
||||
|
||||
if (retries) {
|
||||
/* SKEx registers are stable and can be read */
|
||||
ske_keypad_read_data(keypad);
|
||||
}
|
||||
|
||||
/* enable auto scan interrupts */
|
||||
ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit ske_keypad_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct ske_keypad_platform_data *plat = pdev->dev.platform_data;
|
||||
struct ske_keypad *keypad;
|
||||
struct input_dev *input;
|
||||
struct resource *res;
|
||||
int irq;
|
||||
int error;
|
||||
|
||||
if (!plat) {
|
||||
dev_err(&pdev->dev, "invalid keypad platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "failed to get keypad irq\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "missing platform resources\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
keypad = kzalloc(sizeof(struct ske_keypad), GFP_KERNEL);
|
||||
input = input_allocate_device();
|
||||
if (!keypad || !input) {
|
||||
dev_err(&pdev->dev, "failed to allocate keypad memory\n");
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
keypad->irq = irq;
|
||||
keypad->board = plat;
|
||||
keypad->input = input;
|
||||
spin_lock_init(&keypad->ske_keypad_lock);
|
||||
|
||||
if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
|
||||
dev_err(&pdev->dev, "failed to request I/O memory\n");
|
||||
error = -EBUSY;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
keypad->reg_base = ioremap(res->start, resource_size(res));
|
||||
if (!keypad->reg_base) {
|
||||
dev_err(&pdev->dev, "failed to remap I/O memory\n");
|
||||
error = -ENXIO;
|
||||
goto err_free_mem_region;
|
||||
}
|
||||
|
||||
keypad->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(keypad->clk)) {
|
||||
dev_err(&pdev->dev, "failed to get clk\n");
|
||||
error = PTR_ERR(keypad->clk);
|
||||
goto err_iounmap;
|
||||
}
|
||||
|
||||
input->id.bustype = BUS_HOST;
|
||||
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);
|
||||
|
||||
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 */
|
||||
if (keypad->board->init)
|
||||
keypad->board->init();
|
||||
|
||||
error = ske_keypad_chip_init(keypad);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "unable to init keypad hardware\n");
|
||||
goto err_clk_disable;
|
||||
}
|
||||
|
||||
error = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq,
|
||||
IRQF_ONESHOT, "ske-keypad", keypad);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq);
|
||||
goto err_clk_disable;
|
||||
}
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev,
|
||||
"unable to register input device: %d\n", error);
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
if (plat->wakeup_enable)
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
|
||||
platform_set_drvdata(pdev, keypad);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(keypad->irq, keypad);
|
||||
err_clk_disable:
|
||||
clk_disable(keypad->clk);
|
||||
clk_put(keypad->clk);
|
||||
err_iounmap:
|
||||
iounmap(keypad->reg_base);
|
||||
err_free_mem_region:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
err_free_mem:
|
||||
input_free_device(input);
|
||||
kfree(keypad);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit ske_keypad_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ske_keypad *keypad = platform_get_drvdata(pdev);
|
||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
free_irq(keypad->irq, keypad);
|
||||
|
||||
input_unregister_device(keypad->input);
|
||||
|
||||
clk_disable(keypad->clk);
|
||||
clk_put(keypad->clk);
|
||||
|
||||
if (keypad->board->exit)
|
||||
keypad->board->exit();
|
||||
|
||||
iounmap(keypad->reg_base);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
kfree(keypad);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ske_keypad_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct ske_keypad *keypad = platform_get_drvdata(pdev);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
enable_irq_wake(irq);
|
||||
else
|
||||
ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ske_keypad_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct ske_keypad *keypad = platform_get_drvdata(pdev);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(irq);
|
||||
else
|
||||
ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops ske_keypad_dev_pm_ops = {
|
||||
.suspend = ske_keypad_suspend,
|
||||
.resume = ske_keypad_resume,
|
||||
};
|
||||
#endif
|
||||
|
||||
struct platform_driver ske_keypad_driver = {
|
||||
.driver = {
|
||||
.name = "nmk-ske-keypad",
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &ske_keypad_dev_pm_ops,
|
||||
#endif
|
||||
},
|
||||
.probe = ske_keypad_probe,
|
||||
.remove = __devexit_p(ske_keypad_remove),
|
||||
};
|
||||
|
||||
static int __init ske_keypad_init(void)
|
||||
{
|
||||
return platform_driver_probe(&ske_keypad_driver, ske_keypad_probe);
|
||||
}
|
||||
module_init(ske_keypad_init);
|
||||
|
||||
static void __exit ske_keypad_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ske_keypad_driver);
|
||||
}
|
||||
module_exit(ske_keypad_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Naveen Kumar <naveen.gaddipati@stericsson.com> / Sundar Iyer <sundar.iyer@stericsson.com>");
|
||||
MODULE_DESCRIPTION("Nomadik Scroll-Key-Encoder Keypad Driver");
|
||||
MODULE_ALIAS("platform:nomadik-ske-keypad");
|
318
drivers/input/keyboard/omap4-keypad.c
Normal file
318
drivers/input/keyboard/omap4-keypad.c
Normal file
@ -0,0 +1,318 @@
|
||||
/*
|
||||
* OMAP4 Keypad Driver
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments
|
||||
*
|
||||
* Author: Abraham Arce <x0066660@ti.com>
|
||||
* Initial Code: Syed Rafiuddin <rafiuddin.syed@ti.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <plat/omap4-keypad.h>
|
||||
|
||||
/* OMAP4 registers */
|
||||
#define OMAP4_KBD_REVISION 0x00
|
||||
#define OMAP4_KBD_SYSCONFIG 0x10
|
||||
#define OMAP4_KBD_SYSSTATUS 0x14
|
||||
#define OMAP4_KBD_IRQSTATUS 0x18
|
||||
#define OMAP4_KBD_IRQENABLE 0x1C
|
||||
#define OMAP4_KBD_WAKEUPENABLE 0x20
|
||||
#define OMAP4_KBD_PENDING 0x24
|
||||
#define OMAP4_KBD_CTRL 0x28
|
||||
#define OMAP4_KBD_DEBOUNCINGTIME 0x2C
|
||||
#define OMAP4_KBD_LONGKEYTIME 0x30
|
||||
#define OMAP4_KBD_TIMEOUT 0x34
|
||||
#define OMAP4_KBD_STATEMACHINE 0x38
|
||||
#define OMAP4_KBD_ROWINPUTS 0x3C
|
||||
#define OMAP4_KBD_COLUMNOUTPUTS 0x40
|
||||
#define OMAP4_KBD_FULLCODE31_0 0x44
|
||||
#define OMAP4_KBD_FULLCODE63_32 0x48
|
||||
|
||||
/* OMAP4 bit definitions */
|
||||
#define OMAP4_DEF_IRQENABLE_EVENTEN (1 << 0)
|
||||
#define OMAP4_DEF_IRQENABLE_LONGKEY (1 << 1)
|
||||
#define OMAP4_DEF_IRQENABLE_TIMEOUTEN (1 << 2)
|
||||
#define OMAP4_DEF_WUP_EVENT_ENA (1 << 0)
|
||||
#define OMAP4_DEF_WUP_LONG_KEY_ENA (1 << 1)
|
||||
#define OMAP4_DEF_CTRL_NOSOFTMODE (1 << 1)
|
||||
#define OMAP4_DEF_CTRLPTVVALUE (1 << 2)
|
||||
#define OMAP4_DEF_CTRLPTV (1 << 1)
|
||||
|
||||
/* OMAP4 values */
|
||||
#define OMAP4_VAL_IRQDISABLE 0x00
|
||||
#define OMAP4_VAL_DEBOUNCINGTIME 0x07
|
||||
#define OMAP4_VAL_FUNCTIONALCFG 0x1E
|
||||
|
||||
#define OMAP4_MASK_IRQSTATUSDISABLE 0xFFFF
|
||||
|
||||
struct omap4_keypad {
|
||||
struct input_dev *input;
|
||||
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
|
||||
unsigned int rows;
|
||||
unsigned int cols;
|
||||
unsigned int row_shift;
|
||||
unsigned char key_state[8];
|
||||
unsigned short keymap[];
|
||||
};
|
||||
|
||||
static void __devinit omap4_keypad_config(struct omap4_keypad *keypad_data)
|
||||
{
|
||||
__raw_writel(OMAP4_VAL_FUNCTIONALCFG,
|
||||
keypad_data->base + OMAP4_KBD_CTRL);
|
||||
__raw_writel(OMAP4_VAL_DEBOUNCINGTIME,
|
||||
keypad_data->base + OMAP4_KBD_DEBOUNCINGTIME);
|
||||
__raw_writel(OMAP4_VAL_IRQDISABLE,
|
||||
keypad_data->base + OMAP4_KBD_IRQSTATUS);
|
||||
__raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY,
|
||||
keypad_data->base + OMAP4_KBD_IRQENABLE);
|
||||
__raw_writel(OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA,
|
||||
keypad_data->base + OMAP4_KBD_WAKEUPENABLE);
|
||||
}
|
||||
|
||||
/* Interrupt handler */
|
||||
static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct omap4_keypad *keypad_data = dev_id;
|
||||
struct input_dev *input_dev = keypad_data->input;
|
||||
unsigned char key_state[ARRAY_SIZE(keypad_data->key_state)];
|
||||
unsigned int col, row, code, changed;
|
||||
u32 *new_state = (u32 *) key_state;
|
||||
|
||||
/* Disable interrupts */
|
||||
__raw_writel(OMAP4_VAL_IRQDISABLE,
|
||||
keypad_data->base + OMAP4_KBD_IRQENABLE);
|
||||
|
||||
*new_state = __raw_readl(keypad_data->base + OMAP4_KBD_FULLCODE31_0);
|
||||
*(new_state + 1) = __raw_readl(keypad_data->base
|
||||
+ OMAP4_KBD_FULLCODE63_32);
|
||||
|
||||
for (row = 0; row < keypad_data->rows; row++) {
|
||||
changed = key_state[row] ^ keypad_data->key_state[row];
|
||||
if (!changed)
|
||||
continue;
|
||||
|
||||
for (col = 0; col < keypad_data->cols; col++) {
|
||||
if (changed & (1 << col)) {
|
||||
code = MATRIX_SCAN_CODE(row, col,
|
||||
keypad_data->row_shift);
|
||||
input_event(input_dev, EV_MSC, MSC_SCAN, code);
|
||||
input_report_key(input_dev,
|
||||
keypad_data->keymap[code],
|
||||
key_state[row] & (1 << col));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input_sync(input_dev);
|
||||
|
||||
memcpy(keypad_data->key_state, key_state,
|
||||
sizeof(keypad_data->key_state));
|
||||
|
||||
/* clear pending interrupts */
|
||||
__raw_writel(__raw_readl(keypad_data->base + OMAP4_KBD_IRQSTATUS),
|
||||
keypad_data->base + OMAP4_KBD_IRQSTATUS);
|
||||
|
||||
/* enable interrupts */
|
||||
__raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY,
|
||||
keypad_data->base + OMAP4_KBD_IRQENABLE);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit omap4_keypad_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct omap4_keypad_platform_data *pdata;
|
||||
struct omap4_keypad *keypad_data;
|
||||
struct input_dev *input_dev;
|
||||
struct resource *res;
|
||||
resource_size_t size;
|
||||
unsigned int row_shift, max_keys;
|
||||
int irq;
|
||||
int error;
|
||||
|
||||
/* platform data */
|
||||
pdata = pdev->dev.platform_data;
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "no platform data defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "no base address specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (!irq) {
|
||||
dev_err(&pdev->dev, "no keyboard irq assigned\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!pdata->keymap_data) {
|
||||
dev_err(&pdev->dev, "no keymap data defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
row_shift = get_count_order(pdata->cols);
|
||||
max_keys = pdata->rows << row_shift;
|
||||
|
||||
keypad_data = kzalloc(sizeof(struct omap4_keypad) +
|
||||
max_keys * sizeof(keypad_data->keymap[0]),
|
||||
GFP_KERNEL);
|
||||
if (!keypad_data) {
|
||||
dev_err(&pdev->dev, "keypad_data memory allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
size = resource_size(res);
|
||||
|
||||
res = request_mem_region(res->start, size, pdev->name);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "can't request mem region\n");
|
||||
error = -EBUSY;
|
||||
goto err_free_keypad;
|
||||
}
|
||||
|
||||
keypad_data->base = ioremap(res->start, resource_size(res));
|
||||
if (!keypad_data->base) {
|
||||
dev_err(&pdev->dev, "can't ioremap mem resource\n");
|
||||
error = -ENOMEM;
|
||||
goto err_release_mem;
|
||||
}
|
||||
|
||||
keypad_data->irq = irq;
|
||||
keypad_data->row_shift = row_shift;
|
||||
keypad_data->rows = pdata->rows;
|
||||
keypad_data->cols = pdata->cols;
|
||||
|
||||
/* input device allocation */
|
||||
keypad_data->input = input_dev = input_allocate_device();
|
||||
if (!input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
input_dev->name = pdev->name;
|
||||
input_dev->dev.parent = &pdev->dev;
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->id.vendor = 0x0001;
|
||||
input_dev->id.product = 0x0001;
|
||||
input_dev->id.version = 0x0001;
|
||||
|
||||
input_dev->keycode = keypad_data->keymap;
|
||||
input_dev->keycodesize = sizeof(keypad_data->keymap[0]);
|
||||
input_dev->keycodemax = max_keys;
|
||||
|
||||
__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);
|
||||
|
||||
omap4_keypad_config(keypad_data);
|
||||
|
||||
error = request_irq(keypad_data->irq, omap4_keypad_interrupt,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"omap4-keypad", keypad_data);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to register interrupt\n");
|
||||
goto err_free_input;
|
||||
}
|
||||
|
||||
error = input_register_device(keypad_data->input);
|
||||
if (error < 0) {
|
||||
dev_err(&pdev->dev, "failed to register input device\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
|
||||
platform_set_drvdata(pdev, keypad_data);
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(keypad_data->irq, keypad_data);
|
||||
err_free_input:
|
||||
input_free_device(input_dev);
|
||||
err_unmap:
|
||||
iounmap(keypad_data->base);
|
||||
err_release_mem:
|
||||
release_mem_region(res->start, size);
|
||||
err_free_keypad:
|
||||
kfree(keypad_data);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit omap4_keypad_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
|
||||
struct resource *res;
|
||||
|
||||
free_irq(keypad_data->irq, keypad_data);
|
||||
input_unregister_device(keypad_data->input);
|
||||
|
||||
iounmap(keypad_data->base);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
|
||||
kfree(keypad_data);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver omap4_keypad_driver = {
|
||||
.probe = omap4_keypad_probe,
|
||||
.remove = __devexit_p(omap4_keypad_remove),
|
||||
.driver = {
|
||||
.name = "omap4-keypad",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init omap4_keypad_init(void)
|
||||
{
|
||||
return platform_driver_register(&omap4_keypad_driver);
|
||||
}
|
||||
module_init(omap4_keypad_init);
|
||||
|
||||
static void __exit omap4_keypad_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&omap4_keypad_driver);
|
||||
}
|
||||
module_exit(omap4_keypad_exit);
|
||||
|
||||
MODULE_AUTHOR("Texas Instruments");
|
||||
MODULE_DESCRIPTION("OMAP4 Keypad Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:omap4-keypad");
|
@ -406,23 +406,22 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev)
|
||||
if (error) {
|
||||
dev_info(kp->dbg_dev, "request_irq failed for irq no=%d\n",
|
||||
kp->irq);
|
||||
goto err3;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
/* Enable KP and TO interrupts now. */
|
||||
reg = (u8) ~(KEYP_IMR1_KP | KEYP_IMR1_TO);
|
||||
if (twl4030_kpwrite_u8(kp, reg, KEYP_IMR1)) {
|
||||
error = -EIO;
|
||||
goto err4;
|
||||
goto err3;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, kp);
|
||||
return 0;
|
||||
|
||||
err4:
|
||||
err3:
|
||||
/* mask all events - we don't care about the result */
|
||||
(void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1);
|
||||
err3:
|
||||
free_irq(kp->irq, NULL);
|
||||
err2:
|
||||
input_unregister_device(input);
|
||||
|
@ -22,6 +22,16 @@ config INPUT_88PM860X_ONKEY
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called 88pm860x_onkey.
|
||||
|
||||
config INPUT_AB8500_PONKEY
|
||||
tristate "AB8500 Pon (PowerOn) Key"
|
||||
depends on AB8500_CORE
|
||||
help
|
||||
Say Y here to use the PowerOn Key for ST-Ericsson's AB8500
|
||||
Mix-Sig PMIC.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called ab8500-ponkey.
|
||||
|
||||
config INPUT_AD714X
|
||||
tristate "Analog Devices AD714x Capacitance Touch Sensor"
|
||||
help
|
||||
|
@ -5,6 +5,7 @@
|
||||
# Each configuration option enables a list of files.
|
||||
|
||||
obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o
|
||||
obj-$(CONFIG_INPUT_AB8500_PONKEY) += ab8500-ponkey.o
|
||||
obj-$(CONFIG_INPUT_AD714X) += ad714x.o
|
||||
obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o
|
||||
obj-$(CONFIG_INPUT_AD714X_SPI) += ad714x-spi.o
|
||||
|
157
drivers/input/misc/ab8500-ponkey.c
Normal file
157
drivers/input/misc/ab8500-ponkey.c
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* License Terms: GNU General Public License v2
|
||||
* Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
|
||||
*
|
||||
* AB8500 Power-On Key handler
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/ab8500.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/**
|
||||
* struct ab8500_ponkey - ab8500 ponkey information
|
||||
* @input_dev: pointer to input device
|
||||
* @ab8500: ab8500 parent
|
||||
* @irq_dbf: irq number for falling transition
|
||||
* @irq_dbr: irq number for rising transition
|
||||
*/
|
||||
struct ab8500_ponkey {
|
||||
struct input_dev *idev;
|
||||
struct ab8500 *ab8500;
|
||||
int irq_dbf;
|
||||
int irq_dbr;
|
||||
};
|
||||
|
||||
/* AB8500 gives us an interrupt when ONKEY is held */
|
||||
static irqreturn_t ab8500_ponkey_handler(int irq, void *data)
|
||||
{
|
||||
struct ab8500_ponkey *ponkey = data;
|
||||
|
||||
if (irq == ponkey->irq_dbf)
|
||||
input_report_key(ponkey->idev, KEY_POWER, true);
|
||||
else if (irq == ponkey->irq_dbr)
|
||||
input_report_key(ponkey->idev, KEY_POWER, false);
|
||||
|
||||
input_sync(ponkey->idev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit ab8500_ponkey_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct ab8500_ponkey *ponkey;
|
||||
struct input_dev *input;
|
||||
int irq_dbf, irq_dbr;
|
||||
int error;
|
||||
|
||||
irq_dbf = platform_get_irq_byname(pdev, "ONKEY_DBF");
|
||||
if (irq_dbf < 0) {
|
||||
dev_err(&pdev->dev, "No IRQ for ONKEY_DBF, error=%d\n", irq_dbf);
|
||||
return irq_dbf;
|
||||
}
|
||||
|
||||
irq_dbr = platform_get_irq_byname(pdev, "ONKEY_DBR");
|
||||
if (irq_dbr < 0) {
|
||||
dev_err(&pdev->dev, "No IRQ for ONKEY_DBR, error=%d\n", irq_dbr);
|
||||
return irq_dbr;
|
||||
}
|
||||
|
||||
ponkey = kzalloc(sizeof(struct ab8500_ponkey), GFP_KERNEL);
|
||||
input = input_allocate_device();
|
||||
if (!ponkey || !input) {
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
ponkey->idev = input;
|
||||
ponkey->ab8500 = ab8500;
|
||||
ponkey->irq_dbf = irq_dbf;
|
||||
ponkey->irq_dbr = irq_dbr;
|
||||
|
||||
input->name = "AB8500 POn(PowerOn) Key";
|
||||
input->dev.parent = &pdev->dev;
|
||||
|
||||
input_set_capability(input, EV_KEY, KEY_POWER);
|
||||
|
||||
error = request_any_context_irq(ponkey->irq_dbf, ab8500_ponkey_handler,
|
||||
0, "ab8500-ponkey-dbf", ponkey);
|
||||
if (error < 0) {
|
||||
dev_err(ab8500->dev, "Failed to request dbf IRQ#%d: %d\n",
|
||||
ponkey->irq_dbf, error);
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
error = request_any_context_irq(ponkey->irq_dbr, ab8500_ponkey_handler,
|
||||
0, "ab8500-ponkey-dbr", ponkey);
|
||||
if (error < 0) {
|
||||
dev_err(ab8500->dev, "Failed to request dbr IRQ#%d: %d\n",
|
||||
ponkey->irq_dbr, error);
|
||||
goto err_free_dbf_irq;
|
||||
}
|
||||
|
||||
error = input_register_device(ponkey->idev);
|
||||
if (error) {
|
||||
dev_err(ab8500->dev, "Can't register input device: %d\n", error);
|
||||
goto err_free_dbr_irq;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ponkey);
|
||||
return 0;
|
||||
|
||||
err_free_dbr_irq:
|
||||
free_irq(ponkey->irq_dbr, ponkey);
|
||||
err_free_dbf_irq:
|
||||
free_irq(ponkey->irq_dbf, ponkey);
|
||||
err_free_mem:
|
||||
input_free_device(input);
|
||||
kfree(ponkey);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit ab8500_ponkey_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ab8500_ponkey *ponkey = platform_get_drvdata(pdev);
|
||||
|
||||
free_irq(ponkey->irq_dbf, ponkey);
|
||||
free_irq(ponkey->irq_dbr, ponkey);
|
||||
input_unregister_device(ponkey->idev);
|
||||
kfree(ponkey);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ab8500_ponkey_driver = {
|
||||
.driver = {
|
||||
.name = "ab8500-poweron-key",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ab8500_ponkey_probe,
|
||||
.remove = __devexit_p(ab8500_ponkey_remove),
|
||||
};
|
||||
|
||||
static int __init ab8500_ponkey_init(void)
|
||||
{
|
||||
return platform_driver_register(&ab8500_ponkey_driver);
|
||||
}
|
||||
module_init(ab8500_ponkey_init);
|
||||
|
||||
static void __exit ab8500_ponkey_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ab8500_ponkey_driver);
|
||||
}
|
||||
module_exit(ab8500_ponkey_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Sundar Iyer <sundar.iyer@stericsson.com>");
|
||||
MODULE_DESCRIPTION("ST-Ericsson AB8500 Power-ON(Pon) Key driver");
|
@ -483,51 +483,88 @@ static void ati_remote2_complete_key(struct urb *urb)
|
||||
}
|
||||
|
||||
static int ati_remote2_getkeycode(struct input_dev *idev,
|
||||
unsigned int scancode, unsigned int *keycode)
|
||||
struct input_keymap_entry *ke)
|
||||
{
|
||||
struct ati_remote2 *ar2 = input_get_drvdata(idev);
|
||||
unsigned int mode;
|
||||
int index;
|
||||
int offset;
|
||||
unsigned int index;
|
||||
unsigned int scancode;
|
||||
|
||||
mode = scancode >> 8;
|
||||
if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask))
|
||||
return -EINVAL;
|
||||
if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
|
||||
index = ke->index;
|
||||
if (index >= ATI_REMOTE2_MODES *
|
||||
ARRAY_SIZE(ati_remote2_key_table))
|
||||
return -EINVAL;
|
||||
|
||||
index = ati_remote2_lookup(scancode & 0xFF);
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
mode = ke->index / ARRAY_SIZE(ati_remote2_key_table);
|
||||
offset = ke->index % ARRAY_SIZE(ati_remote2_key_table);
|
||||
scancode = (mode << 8) + ati_remote2_key_table[offset].hw_code;
|
||||
} else {
|
||||
if (input_scancode_to_scalar(ke, &scancode))
|
||||
return -EINVAL;
|
||||
|
||||
mode = scancode >> 8;
|
||||
if (mode > ATI_REMOTE2_PC)
|
||||
return -EINVAL;
|
||||
|
||||
offset = ati_remote2_lookup(scancode & 0xff);
|
||||
if (offset < 0)
|
||||
return -EINVAL;
|
||||
|
||||
index = mode * ARRAY_SIZE(ati_remote2_key_table) + offset;
|
||||
}
|
||||
|
||||
ke->keycode = ar2->keycode[mode][offset];
|
||||
ke->len = sizeof(scancode);
|
||||
memcpy(&ke->scancode, &scancode, sizeof(scancode));
|
||||
ke->index = index;
|
||||
|
||||
*keycode = ar2->keycode[mode][index];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ati_remote2_setkeycode(struct input_dev *idev,
|
||||
unsigned int scancode, unsigned int keycode)
|
||||
const struct input_keymap_entry *ke,
|
||||
unsigned int *old_keycode)
|
||||
{
|
||||
struct ati_remote2 *ar2 = input_get_drvdata(idev);
|
||||
unsigned int mode, old_keycode;
|
||||
int index;
|
||||
unsigned int mode;
|
||||
int offset;
|
||||
unsigned int index;
|
||||
unsigned int scancode;
|
||||
|
||||
mode = scancode >> 8;
|
||||
if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask))
|
||||
return -EINVAL;
|
||||
if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
|
||||
if (ke->index >= ATI_REMOTE2_MODES *
|
||||
ARRAY_SIZE(ati_remote2_key_table))
|
||||
return -EINVAL;
|
||||
|
||||
index = ati_remote2_lookup(scancode & 0xFF);
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
mode = ke->index / ARRAY_SIZE(ati_remote2_key_table);
|
||||
offset = ke->index % ARRAY_SIZE(ati_remote2_key_table);
|
||||
} else {
|
||||
if (input_scancode_to_scalar(ke, &scancode))
|
||||
return -EINVAL;
|
||||
|
||||
old_keycode = ar2->keycode[mode][index];
|
||||
ar2->keycode[mode][index] = keycode;
|
||||
__set_bit(keycode, idev->keybit);
|
||||
mode = scancode >> 8;
|
||||
if (mode > ATI_REMOTE2_PC)
|
||||
return -EINVAL;
|
||||
|
||||
offset = ati_remote2_lookup(scancode & 0xff);
|
||||
if (offset < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*old_keycode = ar2->keycode[mode][offset];
|
||||
ar2->keycode[mode][offset] = ke->keycode;
|
||||
__set_bit(ke->keycode, idev->keybit);
|
||||
|
||||
for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) {
|
||||
for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) {
|
||||
if (ar2->keycode[mode][index] == old_keycode)
|
||||
if (ar2->keycode[mode][index] == *old_keycode)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
__clear_bit(old_keycode, idev->keybit);
|
||||
__clear_bit(*old_keycode, idev->keybit);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -575,8 +612,8 @@ static int ati_remote2_input_init(struct ati_remote2 *ar2)
|
||||
idev->open = ati_remote2_open;
|
||||
idev->close = ati_remote2_close;
|
||||
|
||||
idev->getkeycode = ati_remote2_getkeycode;
|
||||
idev->setkeycode = ati_remote2_setkeycode;
|
||||
idev->getkeycode_new = ati_remote2_getkeycode;
|
||||
idev->setkeycode_new = ati_remote2_setkeycode;
|
||||
|
||||
idev->name = ar2->name;
|
||||
idev->phys = ar2->phys;
|
||||
|
@ -280,7 +280,7 @@ static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_dev
|
||||
|
||||
pm->configcr = kmalloc(sizeof(*(pm->configcr)), GFP_KERNEL);
|
||||
if (!pm->configcr)
|
||||
return -1;
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -699,7 +699,7 @@ int elantech_init(struct psmouse *psmouse)
|
||||
|
||||
psmouse->private = etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL);
|
||||
if (!etd)
|
||||
return -1;
|
||||
return -ENOMEM;
|
||||
|
||||
etd->parity[0] = 1;
|
||||
for (i = 1; i < 256; i++)
|
||||
|
@ -1584,10 +1584,10 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
|
||||
if (!new_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
while (serio->child) {
|
||||
while (!list_empty(&serio->children)) {
|
||||
if (++retry > 3) {
|
||||
printk(KERN_WARNING
|
||||
"psmouse: failed to destroy child port, "
|
||||
"psmouse: failed to destroy children ports, "
|
||||
"protocol change aborted.\n");
|
||||
input_free_device(new_dev);
|
||||
return -EIO;
|
||||
|
@ -294,7 +294,29 @@ static int synaptics_pt_write(struct serio *serio, unsigned char c)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int synaptics_is_pt_packet(unsigned char *buf)
|
||||
static int synaptics_pt_start(struct serio *serio)
|
||||
{
|
||||
struct psmouse *parent = serio_get_drvdata(serio->parent);
|
||||
struct synaptics_data *priv = parent->private;
|
||||
|
||||
serio_pause_rx(parent->ps2dev.serio);
|
||||
priv->pt_port = serio;
|
||||
serio_continue_rx(parent->ps2dev.serio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void synaptics_pt_stop(struct serio *serio)
|
||||
{
|
||||
struct psmouse *parent = serio_get_drvdata(serio->parent);
|
||||
struct synaptics_data *priv = parent->private;
|
||||
|
||||
serio_pause_rx(parent->ps2dev.serio);
|
||||
priv->pt_port = NULL;
|
||||
serio_continue_rx(parent->ps2dev.serio);
|
||||
}
|
||||
|
||||
static int synaptics_is_pt_packet(unsigned char *buf)
|
||||
{
|
||||
return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4;
|
||||
}
|
||||
@ -315,9 +337,8 @@ static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet
|
||||
|
||||
static void synaptics_pt_activate(struct psmouse *psmouse)
|
||||
{
|
||||
struct serio *ptport = psmouse->ps2dev.serio->child;
|
||||
struct psmouse *child = serio_get_drvdata(ptport);
|
||||
struct synaptics_data *priv = psmouse->private;
|
||||
struct psmouse *child = serio_get_drvdata(priv->pt_port);
|
||||
|
||||
/* adjust the touchpad to child's choice of protocol */
|
||||
if (child) {
|
||||
@ -345,6 +366,8 @@ static void synaptics_pt_create(struct psmouse *psmouse)
|
||||
strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
|
||||
strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name));
|
||||
serio->write = synaptics_pt_write;
|
||||
serio->start = synaptics_pt_start;
|
||||
serio->stop = synaptics_pt_stop;
|
||||
serio->parent = psmouse->ps2dev.serio;
|
||||
|
||||
psmouse->pt_activate = synaptics_pt_activate;
|
||||
@ -578,9 +601,10 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
|
||||
if (unlikely(priv->pkt_type == SYN_NEWABS))
|
||||
priv->pkt_type = synaptics_detect_pkt_type(psmouse);
|
||||
|
||||
if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) {
|
||||
if (psmouse->ps2dev.serio->child)
|
||||
synaptics_pass_pt_packet(psmouse->ps2dev.serio->child, psmouse->packet);
|
||||
if (SYN_CAP_PASS_THROUGH(priv->capabilities) &&
|
||||
synaptics_is_pt_packet(psmouse->packet)) {
|
||||
if (priv->pt_port)
|
||||
synaptics_pass_pt_packet(priv->pt_port, psmouse->packet);
|
||||
} else
|
||||
synaptics_process_packet(psmouse);
|
||||
|
||||
@ -731,7 +755,7 @@ int synaptics_init(struct psmouse *psmouse)
|
||||
|
||||
psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -1;
|
||||
return -ENOMEM;
|
||||
|
||||
psmouse_reset(psmouse);
|
||||
|
||||
|
@ -110,6 +110,8 @@ struct synaptics_data {
|
||||
unsigned char pkt_type; /* packet type - old, new, etc */
|
||||
unsigned char mode; /* current mode byte */
|
||||
int scroll;
|
||||
|
||||
struct serio *pt_port; /* Pass-through serio port */
|
||||
};
|
||||
|
||||
void synaptics_module_init(void);
|
||||
|
@ -303,7 +303,7 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
|
||||
|
||||
psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL);
|
||||
if (!psmouse->private)
|
||||
return -1;
|
||||
return -ENOMEM;
|
||||
|
||||
psmouse->vendor = "IBM";
|
||||
psmouse->name = "TrackPoint";
|
||||
|
@ -866,7 +866,7 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
|
||||
spin_lock_init(&mousedev->client_lock);
|
||||
mutex_init(&mousedev->mutex);
|
||||
lockdep_set_subclass(&mousedev->mutex,
|
||||
minor == MOUSEDEV_MIX ? MOUSEDEV_MIX : 0);
|
||||
minor == MOUSEDEV_MIX ? SINGLE_DEPTH_NESTING : 0);
|
||||
init_waitqueue_head(&mousedev->wait);
|
||||
|
||||
if (minor == MOUSEDEV_MIX)
|
||||
|
@ -226,4 +226,13 @@ config SERIO_AMS_DELTA
|
||||
To compile this driver as a module, choose M here;
|
||||
the module will be called ams_delta_serio.
|
||||
|
||||
config SERIO_PS2MULT
|
||||
tristate "TQC PS/2 multiplexer"
|
||||
help
|
||||
Say Y here if you have the PS/2 line multiplexer like the one
|
||||
present on TQC boads.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ps2mult.
|
||||
|
||||
endif
|
||||
|
@ -18,6 +18,7 @@ obj-$(CONFIG_SERIO_GSCPS2) += gscps2.o
|
||||
obj-$(CONFIG_HP_SDC) += hp_sdc.o
|
||||
obj-$(CONFIG_HIL_MLC) += hp_sdc_mlc.o hil_mlc.o
|
||||
obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o
|
||||
obj-$(CONFIG_SERIO_PS2MULT) += ps2mult.o
|
||||
obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o
|
||||
obj-$(CONFIG_SERIO_LIBPS2) += libps2.o
|
||||
obj-$(CONFIG_SERIO_RAW) += serio_raw.o
|
||||
|
@ -1063,7 +1063,7 @@ static long i8042_panic_blink(int state)
|
||||
#ifdef CONFIG_X86
|
||||
static void i8042_dritek_enable(void)
|
||||
{
|
||||
char param = 0x90;
|
||||
unsigned char param = 0x90;
|
||||
int error;
|
||||
|
||||
error = i8042_command(¶m, 0x1059);
|
||||
|
318
drivers/input/serio/ps2mult.c
Normal file
318
drivers/input/serio/ps2mult.c
Normal file
@ -0,0 +1,318 @@
|
||||
/*
|
||||
* TQC PS/2 Multiplexer driver
|
||||
*
|
||||
* Copyright (C) 2010 Dmitry Eremin-Solenikov
|
||||
*
|
||||
* 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 <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/serio.h>
|
||||
|
||||
MODULE_AUTHOR("Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>");
|
||||
MODULE_DESCRIPTION("TQC PS/2 Multiplexer driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define PS2MULT_KB_SELECTOR 0xA0
|
||||
#define PS2MULT_MS_SELECTOR 0xA1
|
||||
#define PS2MULT_ESCAPE 0x7D
|
||||
#define PS2MULT_BSYNC 0x7E
|
||||
#define PS2MULT_SESSION_START 0x55
|
||||
#define PS2MULT_SESSION_END 0x56
|
||||
|
||||
struct ps2mult_port {
|
||||
struct serio *serio;
|
||||
unsigned char sel;
|
||||
bool registered;
|
||||
};
|
||||
|
||||
#define PS2MULT_NUM_PORTS 2
|
||||
#define PS2MULT_KBD_PORT 0
|
||||
#define PS2MULT_MOUSE_PORT 1
|
||||
|
||||
struct ps2mult {
|
||||
struct serio *mx_serio;
|
||||
struct ps2mult_port ports[PS2MULT_NUM_PORTS];
|
||||
|
||||
spinlock_t lock;
|
||||
struct ps2mult_port *in_port;
|
||||
struct ps2mult_port *out_port;
|
||||
bool escape;
|
||||
};
|
||||
|
||||
/* First MUST come PS2MULT_NUM_PORTS selectors */
|
||||
static const unsigned char ps2mult_controls[] = {
|
||||
PS2MULT_KB_SELECTOR, PS2MULT_MS_SELECTOR,
|
||||
PS2MULT_ESCAPE, PS2MULT_BSYNC,
|
||||
PS2MULT_SESSION_START, PS2MULT_SESSION_END,
|
||||
};
|
||||
|
||||
static const struct serio_device_id ps2mult_serio_ids[] = {
|
||||
{
|
||||
.type = SERIO_RS232,
|
||||
.proto = SERIO_PS2MULT,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(serio, ps2mult_serio_ids);
|
||||
|
||||
static void ps2mult_select_port(struct ps2mult *psm, struct ps2mult_port *port)
|
||||
{
|
||||
struct serio *mx_serio = psm->mx_serio;
|
||||
|
||||
serio_write(mx_serio, port->sel);
|
||||
psm->out_port = port;
|
||||
dev_dbg(&mx_serio->dev, "switched to sel %02x\n", port->sel);
|
||||
}
|
||||
|
||||
static int ps2mult_serio_write(struct serio *serio, unsigned char data)
|
||||
{
|
||||
struct serio *mx_port = serio->parent;
|
||||
struct ps2mult *psm = serio_get_drvdata(mx_port);
|
||||
struct ps2mult_port *port = serio->port_data;
|
||||
bool need_escape;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&psm->lock, flags);
|
||||
|
||||
if (psm->out_port != port)
|
||||
ps2mult_select_port(psm, port);
|
||||
|
||||
need_escape = memchr(ps2mult_controls, data, sizeof(ps2mult_controls));
|
||||
|
||||
dev_dbg(&serio->dev,
|
||||
"write: %s%02x\n", need_escape ? "ESC " : "", data);
|
||||
|
||||
if (need_escape)
|
||||
serio_write(mx_port, PS2MULT_ESCAPE);
|
||||
|
||||
serio_write(mx_port, data);
|
||||
|
||||
spin_unlock_irqrestore(&psm->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ps2mult_serio_start(struct serio *serio)
|
||||
{
|
||||
struct ps2mult *psm = serio_get_drvdata(serio->parent);
|
||||
struct ps2mult_port *port = serio->port_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&psm->lock, flags);
|
||||
port->registered = true;
|
||||
spin_unlock_irqrestore(&psm->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ps2mult_serio_stop(struct serio *serio)
|
||||
{
|
||||
struct ps2mult *psm = serio_get_drvdata(serio->parent);
|
||||
struct ps2mult_port *port = serio->port_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&psm->lock, flags);
|
||||
port->registered = false;
|
||||
spin_unlock_irqrestore(&psm->lock, flags);
|
||||
}
|
||||
|
||||
static int ps2mult_create_port(struct ps2mult *psm, int i)
|
||||
{
|
||||
struct serio *mx_serio = psm->mx_serio;
|
||||
struct serio *serio;
|
||||
|
||||
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
|
||||
if (!serio)
|
||||
return -ENOMEM;
|
||||
|
||||
strlcpy(serio->name, "TQC PS/2 Multiplexer", sizeof(serio->name));
|
||||
snprintf(serio->phys, sizeof(serio->phys),
|
||||
"%s/port%d", mx_serio->phys, i);
|
||||
serio->id.type = SERIO_8042;
|
||||
serio->write = ps2mult_serio_write;
|
||||
serio->start = ps2mult_serio_start;
|
||||
serio->stop = ps2mult_serio_stop;
|
||||
serio->parent = psm->mx_serio;
|
||||
serio->port_data = &psm->ports[i];
|
||||
|
||||
psm->ports[i].serio = serio;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ps2mult_reset(struct ps2mult *psm)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&psm->lock, flags);
|
||||
|
||||
serio_write(psm->mx_serio, PS2MULT_SESSION_END);
|
||||
serio_write(psm->mx_serio, PS2MULT_SESSION_START);
|
||||
|
||||
ps2mult_select_port(psm, &psm->ports[PS2MULT_KBD_PORT]);
|
||||
|
||||
spin_unlock_irqrestore(&psm->lock, flags);
|
||||
}
|
||||
|
||||
static int ps2mult_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct ps2mult *psm;
|
||||
int i;
|
||||
int error;
|
||||
|
||||
if (!serio->write)
|
||||
return -EINVAL;
|
||||
|
||||
psm = kzalloc(sizeof(*psm), GFP_KERNEL);
|
||||
if (!psm)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&psm->lock);
|
||||
psm->mx_serio = serio;
|
||||
|
||||
for (i = 0; i < PS2MULT_NUM_PORTS; i++) {
|
||||
psm->ports[i].sel = ps2mult_controls[i];
|
||||
error = ps2mult_create_port(psm, i);
|
||||
if (error)
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
psm->in_port = psm->out_port = &psm->ports[PS2MULT_KBD_PORT];
|
||||
|
||||
serio_set_drvdata(serio, psm);
|
||||
error = serio_open(serio, drv);
|
||||
if (error)
|
||||
goto err_out;
|
||||
|
||||
ps2mult_reset(psm);
|
||||
|
||||
for (i = 0; i < PS2MULT_NUM_PORTS; i++) {
|
||||
struct serio *s = psm->ports[i].serio;
|
||||
|
||||
dev_info(&serio->dev, "%s port at %s\n", s->name, serio->phys);
|
||||
serio_register_port(s);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
while (--i >= 0)
|
||||
kfree(psm->ports[i].serio);
|
||||
kfree(serio);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void ps2mult_disconnect(struct serio *serio)
|
||||
{
|
||||
struct ps2mult *psm = serio_get_drvdata(serio);
|
||||
|
||||
/* Note that serio core already take care of children ports */
|
||||
serio_write(serio, PS2MULT_SESSION_END);
|
||||
serio_close(serio);
|
||||
kfree(psm);
|
||||
|
||||
serio_set_drvdata(serio, NULL);
|
||||
}
|
||||
|
||||
static int ps2mult_reconnect(struct serio *serio)
|
||||
{
|
||||
struct ps2mult *psm = serio_get_drvdata(serio);
|
||||
|
||||
ps2mult_reset(psm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t ps2mult_interrupt(struct serio *serio,
|
||||
unsigned char data, unsigned int dfl)
|
||||
{
|
||||
struct ps2mult *psm = serio_get_drvdata(serio);
|
||||
struct ps2mult_port *in_port;
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, dfl);
|
||||
|
||||
spin_lock_irqsave(&psm->lock, flags);
|
||||
|
||||
if (psm->escape) {
|
||||
psm->escape = false;
|
||||
in_port = psm->in_port;
|
||||
if (in_port->registered)
|
||||
serio_interrupt(in_port->serio, data, dfl);
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (data) {
|
||||
case PS2MULT_ESCAPE:
|
||||
dev_dbg(&serio->dev, "ESCAPE\n");
|
||||
psm->escape = true;
|
||||
break;
|
||||
|
||||
case PS2MULT_BSYNC:
|
||||
dev_dbg(&serio->dev, "BSYNC\n");
|
||||
psm->in_port = psm->out_port;
|
||||
break;
|
||||
|
||||
case PS2MULT_SESSION_START:
|
||||
dev_dbg(&serio->dev, "SS\n");
|
||||
break;
|
||||
|
||||
case PS2MULT_SESSION_END:
|
||||
dev_dbg(&serio->dev, "SE\n");
|
||||
break;
|
||||
|
||||
case PS2MULT_KB_SELECTOR:
|
||||
dev_dbg(&serio->dev, "KB\n");
|
||||
psm->in_port = &psm->ports[PS2MULT_KBD_PORT];
|
||||
break;
|
||||
|
||||
case PS2MULT_MS_SELECTOR:
|
||||
dev_dbg(&serio->dev, "MS\n");
|
||||
psm->in_port = &psm->ports[PS2MULT_MOUSE_PORT];
|
||||
break;
|
||||
|
||||
default:
|
||||
in_port = psm->in_port;
|
||||
if (in_port->registered)
|
||||
serio_interrupt(in_port->serio, data, dfl);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&psm->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static struct serio_driver ps2mult_drv = {
|
||||
.driver = {
|
||||
.name = "ps2mult",
|
||||
},
|
||||
.description = "TQC PS/2 Multiplexer driver",
|
||||
.id_table = ps2mult_serio_ids,
|
||||
.interrupt = ps2mult_interrupt,
|
||||
.connect = ps2mult_connect,
|
||||
.disconnect = ps2mult_disconnect,
|
||||
.reconnect = ps2mult_reconnect,
|
||||
};
|
||||
|
||||
static int __init ps2mult_init(void)
|
||||
{
|
||||
return serio_register_driver(&ps2mult_drv);
|
||||
}
|
||||
|
||||
static void __exit ps2mult_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&ps2mult_drv);
|
||||
}
|
||||
|
||||
module_init(ps2mult_init);
|
||||
module_exit(ps2mult_exit);
|
@ -37,7 +37,6 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/freezer.h>
|
||||
|
||||
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
|
||||
MODULE_DESCRIPTION("Serio abstraction core");
|
||||
@ -56,7 +55,7 @@ static struct bus_type serio_bus;
|
||||
static void serio_add_port(struct serio *serio);
|
||||
static int serio_reconnect_port(struct serio *serio);
|
||||
static void serio_disconnect_port(struct serio *serio);
|
||||
static void serio_reconnect_chain(struct serio *serio);
|
||||
static void serio_reconnect_subtree(struct serio *serio);
|
||||
static void serio_attach_driver(struct serio_driver *drv);
|
||||
|
||||
static int serio_connect_driver(struct serio *serio, struct serio_driver *drv)
|
||||
@ -152,7 +151,7 @@ static void serio_find_driver(struct serio *serio)
|
||||
enum serio_event_type {
|
||||
SERIO_RESCAN_PORT,
|
||||
SERIO_RECONNECT_PORT,
|
||||
SERIO_RECONNECT_CHAIN,
|
||||
SERIO_RECONNECT_SUBTREE,
|
||||
SERIO_REGISTER_PORT,
|
||||
SERIO_ATTACH_DRIVER,
|
||||
};
|
||||
@ -292,8 +291,8 @@ static void serio_handle_event(void)
|
||||
serio_find_driver(event->object);
|
||||
break;
|
||||
|
||||
case SERIO_RECONNECT_CHAIN:
|
||||
serio_reconnect_chain(event->object);
|
||||
case SERIO_RECONNECT_SUBTREE:
|
||||
serio_reconnect_subtree(event->object);
|
||||
break;
|
||||
|
||||
case SERIO_ATTACH_DRIVER:
|
||||
@ -330,12 +329,10 @@ static void serio_remove_pending_events(void *object)
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy child serio port (if any) that has not been fully registered yet.
|
||||
* Locate child serio port (if any) that has not been fully registered yet.
|
||||
*
|
||||
* Note that we rely on the fact that port can have only one child and therefore
|
||||
* only one child registration request can be pending. Additionally, children
|
||||
* are registered by driver's connect() handler so there can't be a grandchild
|
||||
* pending registration together with a child.
|
||||
* Children are registered by driver's connect() handler so there can't be a
|
||||
* grandchild pending registration together with a child.
|
||||
*/
|
||||
static struct serio *serio_get_pending_child(struct serio *parent)
|
||||
{
|
||||
@ -449,7 +446,7 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *
|
||||
if (!strncmp(buf, "none", count)) {
|
||||
serio_disconnect_port(serio);
|
||||
} else if (!strncmp(buf, "reconnect", count)) {
|
||||
serio_reconnect_chain(serio);
|
||||
serio_reconnect_subtree(serio);
|
||||
} else if (!strncmp(buf, "rescan", count)) {
|
||||
serio_disconnect_port(serio);
|
||||
serio_find_driver(serio);
|
||||
@ -516,6 +513,8 @@ static void serio_init_port(struct serio *serio)
|
||||
__module_get(THIS_MODULE);
|
||||
|
||||
INIT_LIST_HEAD(&serio->node);
|
||||
INIT_LIST_HEAD(&serio->child_node);
|
||||
INIT_LIST_HEAD(&serio->children);
|
||||
spin_lock_init(&serio->lock);
|
||||
mutex_init(&serio->drv_mutex);
|
||||
device_initialize(&serio->dev);
|
||||
@ -538,12 +537,13 @@ static void serio_init_port(struct serio *serio)
|
||||
*/
|
||||
static void serio_add_port(struct serio *serio)
|
||||
{
|
||||
struct serio *parent = serio->parent;
|
||||
int error;
|
||||
|
||||
if (serio->parent) {
|
||||
serio_pause_rx(serio->parent);
|
||||
serio->parent->child = serio;
|
||||
serio_continue_rx(serio->parent);
|
||||
if (parent) {
|
||||
serio_pause_rx(parent);
|
||||
list_add_tail(&serio->child_node, &parent->children);
|
||||
serio_continue_rx(parent);
|
||||
}
|
||||
|
||||
list_add_tail(&serio->node, &serio_list);
|
||||
@ -559,15 +559,14 @@ static void serio_add_port(struct serio *serio)
|
||||
}
|
||||
|
||||
/*
|
||||
* serio_destroy_port() completes deregistration process and removes
|
||||
* serio_destroy_port() completes unregistration process and removes
|
||||
* port from the system
|
||||
*/
|
||||
static void serio_destroy_port(struct serio *serio)
|
||||
{
|
||||
struct serio *child;
|
||||
|
||||
child = serio_get_pending_child(serio);
|
||||
if (child) {
|
||||
while ((child = serio_get_pending_child(serio)) != NULL) {
|
||||
serio_remove_pending_events(child);
|
||||
put_device(&child->dev);
|
||||
}
|
||||
@ -577,7 +576,7 @@ static void serio_destroy_port(struct serio *serio)
|
||||
|
||||
if (serio->parent) {
|
||||
serio_pause_rx(serio->parent);
|
||||
serio->parent->child = NULL;
|
||||
list_del_init(&serio->child_node);
|
||||
serio_continue_rx(serio->parent);
|
||||
serio->parent = NULL;
|
||||
}
|
||||
@ -609,46 +608,82 @@ static int serio_reconnect_port(struct serio *serio)
|
||||
}
|
||||
|
||||
/*
|
||||
* Reconnect serio port and all its children (re-initialize attached devices)
|
||||
* Reconnect serio port and all its children (re-initialize attached
|
||||
* devices).
|
||||
*/
|
||||
static void serio_reconnect_chain(struct serio *serio)
|
||||
static void serio_reconnect_subtree(struct serio *root)
|
||||
{
|
||||
struct serio *s = root;
|
||||
int error;
|
||||
|
||||
do {
|
||||
if (serio_reconnect_port(serio)) {
|
||||
/* Ok, old children are now gone, we are done */
|
||||
break;
|
||||
error = serio_reconnect_port(s);
|
||||
if (!error) {
|
||||
/*
|
||||
* Reconnect was successful, move on to do the
|
||||
* first child.
|
||||
*/
|
||||
if (!list_empty(&s->children)) {
|
||||
s = list_first_entry(&s->children,
|
||||
struct serio, child_node);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
serio = serio->child;
|
||||
} while (serio);
|
||||
|
||||
/*
|
||||
* Either it was a leaf node or reconnect failed and it
|
||||
* became a leaf node. Continue reconnecting starting with
|
||||
* the next sibling of the parent node.
|
||||
*/
|
||||
while (s != root) {
|
||||
struct serio *parent = s->parent;
|
||||
|
||||
if (!list_is_last(&s->child_node, &parent->children)) {
|
||||
s = list_entry(s->child_node.next,
|
||||
struct serio, child_node);
|
||||
break;
|
||||
}
|
||||
|
||||
s = parent;
|
||||
}
|
||||
} while (s != root);
|
||||
}
|
||||
|
||||
/*
|
||||
* serio_disconnect_port() unbinds a port from its driver. As a side effect
|
||||
* all child ports are unbound and destroyed.
|
||||
* all children ports are unbound and destroyed.
|
||||
*/
|
||||
static void serio_disconnect_port(struct serio *serio)
|
||||
{
|
||||
struct serio *s, *parent;
|
||||
struct serio *s = serio;
|
||||
|
||||
/*
|
||||
* Children ports should be disconnected and destroyed
|
||||
* first; we travel the tree in depth-first order.
|
||||
*/
|
||||
while (!list_empty(&serio->children)) {
|
||||
|
||||
/* Locate a leaf */
|
||||
while (!list_empty(&s->children))
|
||||
s = list_first_entry(&s->children,
|
||||
struct serio, child_node);
|
||||
|
||||
if (serio->child) {
|
||||
/*
|
||||
* Children ports should be disconnected and destroyed
|
||||
* first, staring with the leaf one, since we don't want
|
||||
* to do recursion
|
||||
* Prune this leaf node unless it is the one we
|
||||
* started with.
|
||||
*/
|
||||
for (s = serio; s->child; s = s->child)
|
||||
/* empty */;
|
||||
|
||||
do {
|
||||
parent = s->parent;
|
||||
if (s != serio) {
|
||||
struct serio *parent = s->parent;
|
||||
|
||||
device_release_driver(&s->dev);
|
||||
serio_destroy_port(s);
|
||||
} while ((s = parent) != serio);
|
||||
|
||||
s = parent;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, no children left, now disconnect this port
|
||||
* OK, no children left, now disconnect this port.
|
||||
*/
|
||||
device_release_driver(&serio->dev);
|
||||
}
|
||||
@ -661,7 +696,7 @@ EXPORT_SYMBOL(serio_rescan);
|
||||
|
||||
void serio_reconnect(struct serio *serio)
|
||||
{
|
||||
serio_queue_event(serio, NULL, SERIO_RECONNECT_CHAIN);
|
||||
serio_queue_event(serio, NULL, SERIO_RECONNECT_SUBTREE);
|
||||
}
|
||||
EXPORT_SYMBOL(serio_reconnect);
|
||||
|
||||
@ -689,14 +724,16 @@ void serio_unregister_port(struct serio *serio)
|
||||
EXPORT_SYMBOL(serio_unregister_port);
|
||||
|
||||
/*
|
||||
* Safely unregisters child port if one is present.
|
||||
* Safely unregisters children ports if they are present.
|
||||
*/
|
||||
void serio_unregister_child_port(struct serio *serio)
|
||||
{
|
||||
struct serio *s, *next;
|
||||
|
||||
mutex_lock(&serio_mutex);
|
||||
if (serio->child) {
|
||||
serio_disconnect_port(serio->child);
|
||||
serio_destroy_port(serio->child);
|
||||
list_for_each_entry_safe(s, next, &serio->children, child_node) {
|
||||
serio_disconnect_port(s);
|
||||
serio_destroy_port(s);
|
||||
}
|
||||
mutex_unlock(&serio_mutex);
|
||||
}
|
||||
|
@ -22,6 +22,37 @@ MODULE_DESCRIPTION("Generic support for sparse keymaps");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_VERSION("0.1");
|
||||
|
||||
static unsigned int sparse_keymap_get_key_index(struct input_dev *dev,
|
||||
const struct key_entry *k)
|
||||
{
|
||||
struct key_entry *key;
|
||||
unsigned int idx = 0;
|
||||
|
||||
for (key = dev->keycode; key->type != KE_END; key++) {
|
||||
if (key->type == KE_KEY) {
|
||||
if (key == k)
|
||||
break;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
static struct key_entry *sparse_keymap_entry_by_index(struct input_dev *dev,
|
||||
unsigned int index)
|
||||
{
|
||||
struct key_entry *key;
|
||||
unsigned int key_cnt = 0;
|
||||
|
||||
for (key = dev->keycode; key->type != KE_END; key++)
|
||||
if (key->type == KE_KEY)
|
||||
if (key_cnt++ == index)
|
||||
return key;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* sparse_keymap_entry_from_scancode - perform sparse keymap lookup
|
||||
* @dev: Input device using sparse keymap
|
||||
@ -64,16 +95,36 @@ struct key_entry *sparse_keymap_entry_from_keycode(struct input_dev *dev,
|
||||
}
|
||||
EXPORT_SYMBOL(sparse_keymap_entry_from_keycode);
|
||||
|
||||
static struct key_entry *sparse_keymap_locate(struct input_dev *dev,
|
||||
const struct input_keymap_entry *ke)
|
||||
{
|
||||
struct key_entry *key;
|
||||
unsigned int scancode;
|
||||
|
||||
if (ke->flags & INPUT_KEYMAP_BY_INDEX)
|
||||
key = sparse_keymap_entry_by_index(dev, ke->index);
|
||||
else if (input_scancode_to_scalar(ke, &scancode) == 0)
|
||||
key = sparse_keymap_entry_from_scancode(dev, scancode);
|
||||
else
|
||||
key = NULL;
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
static int sparse_keymap_getkeycode(struct input_dev *dev,
|
||||
unsigned int scancode,
|
||||
unsigned int *keycode)
|
||||
struct input_keymap_entry *ke)
|
||||
{
|
||||
const struct key_entry *key;
|
||||
|
||||
if (dev->keycode) {
|
||||
key = sparse_keymap_entry_from_scancode(dev, scancode);
|
||||
key = sparse_keymap_locate(dev, ke);
|
||||
if (key && key->type == KE_KEY) {
|
||||
*keycode = key->keycode;
|
||||
ke->keycode = key->keycode;
|
||||
if (!(ke->flags & INPUT_KEYMAP_BY_INDEX))
|
||||
ke->index =
|
||||
sparse_keymap_get_key_index(dev, key);
|
||||
ke->len = sizeof(key->code);
|
||||
memcpy(ke->scancode, &key->code, sizeof(key->code));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -82,20 +133,19 @@ static int sparse_keymap_getkeycode(struct input_dev *dev,
|
||||
}
|
||||
|
||||
static int sparse_keymap_setkeycode(struct input_dev *dev,
|
||||
unsigned int scancode,
|
||||
unsigned int keycode)
|
||||
const struct input_keymap_entry *ke,
|
||||
unsigned int *old_keycode)
|
||||
{
|
||||
struct key_entry *key;
|
||||
int old_keycode;
|
||||
|
||||
if (dev->keycode) {
|
||||
key = sparse_keymap_entry_from_scancode(dev, scancode);
|
||||
key = sparse_keymap_locate(dev, ke);
|
||||
if (key && key->type == KE_KEY) {
|
||||
old_keycode = key->keycode;
|
||||
key->keycode = keycode;
|
||||
set_bit(keycode, dev->keybit);
|
||||
if (!sparse_keymap_entry_from_keycode(dev, old_keycode))
|
||||
clear_bit(old_keycode, dev->keybit);
|
||||
*old_keycode = key->keycode;
|
||||
key->keycode = ke->keycode;
|
||||
set_bit(ke->keycode, dev->keybit);
|
||||
if (!sparse_keymap_entry_from_keycode(dev, *old_keycode))
|
||||
clear_bit(*old_keycode, dev->keybit);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -159,15 +209,14 @@ int sparse_keymap_setup(struct input_dev *dev,
|
||||
|
||||
dev->keycode = map;
|
||||
dev->keycodemax = map_size;
|
||||
dev->getkeycode = sparse_keymap_getkeycode;
|
||||
dev->setkeycode = sparse_keymap_setkeycode;
|
||||
dev->getkeycode_new = sparse_keymap_getkeycode;
|
||||
dev->setkeycode_new = sparse_keymap_setkeycode;
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
kfree(map);
|
||||
return error;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL(sparse_keymap_setup);
|
||||
|
||||
|
@ -49,6 +49,17 @@ config TABLET_USB_GTCO
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called gtco.
|
||||
|
||||
config TABLET_USB_HANWANG
|
||||
tristate "Hanwang Art Master III tablet support (USB)"
|
||||
depends on USB_ARCH_HAS_HCD
|
||||
select USB
|
||||
help
|
||||
Say Y here if you want to use the USB version of the Hanwang Art
|
||||
Master III tablet.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called hanwang.
|
||||
|
||||
config TABLET_USB_KBTAB
|
||||
tristate "KB Gear JamStudio tablet support (USB)"
|
||||
depends on USB_ARCH_HAS_HCD
|
||||
|
@ -8,5 +8,6 @@ wacom-objs := wacom_wac.o wacom_sys.o
|
||||
obj-$(CONFIG_TABLET_USB_ACECAD) += acecad.o
|
||||
obj-$(CONFIG_TABLET_USB_AIPTEK) += aiptek.o
|
||||
obj-$(CONFIG_TABLET_USB_GTCO) += gtco.o
|
||||
obj-$(CONFIG_TABLET_USB_HANWANG) += hanwang.o
|
||||
obj-$(CONFIG_TABLET_USB_KBTAB) += kbtab.o
|
||||
obj-$(CONFIG_TABLET_USB_WACOM) += wacom.o
|
||||
|
446
drivers/input/tablet/hanwang.c
Normal file
446
drivers/input/tablet/hanwang.c
Normal file
@ -0,0 +1,446 @@
|
||||
/*
|
||||
* USB Hanwang tablet support
|
||||
*
|
||||
* Copyright (c) 2010 Xing Wei <weixing@hanwang.com.cn>
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
#define DRIVER_AUTHOR "Xing Wei <weixing@hanwang.com.cn>"
|
||||
#define DRIVER_DESC "USB Hanwang tablet driver"
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE(DRIVER_LICENSE);
|
||||
|
||||
#define USB_VENDOR_ID_HANWANG 0x0b57
|
||||
#define HANWANG_TABLET_INT_CLASS 0x0003
|
||||
#define HANWANG_TABLET_INT_SUB_CLASS 0x0001
|
||||
#define HANWANG_TABLET_INT_PROTOCOL 0x0002
|
||||
|
||||
#define ART_MASTER_PKGLEN_MAX 10
|
||||
|
||||
/* device IDs */
|
||||
#define STYLUS_DEVICE_ID 0x02
|
||||
#define TOUCH_DEVICE_ID 0x03
|
||||
#define CURSOR_DEVICE_ID 0x06
|
||||
#define ERASER_DEVICE_ID 0x0A
|
||||
#define PAD_DEVICE_ID 0x0F
|
||||
|
||||
/* match vendor and interface info */
|
||||
#define HANWANG_TABLET_DEVICE(vend, cl, sc, pr) \
|
||||
.match_flags = USB_DEVICE_ID_MATCH_VENDOR \
|
||||
| USB_DEVICE_ID_MATCH_INT_INFO, \
|
||||
.idVendor = (vend), \
|
||||
.bInterfaceClass = (cl), \
|
||||
.bInterfaceSubClass = (sc), \
|
||||
.bInterfaceProtocol = (pr)
|
||||
|
||||
enum hanwang_tablet_type {
|
||||
HANWANG_ART_MASTER_III,
|
||||
HANWANG_ART_MASTER_HD,
|
||||
};
|
||||
|
||||
struct hanwang {
|
||||
unsigned char *data;
|
||||
dma_addr_t data_dma;
|
||||
struct input_dev *dev;
|
||||
struct usb_device *usbdev;
|
||||
struct urb *irq;
|
||||
const struct hanwang_features *features;
|
||||
unsigned int current_tool;
|
||||
unsigned int current_id;
|
||||
char name[64];
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
struct hanwang_features {
|
||||
unsigned short pid;
|
||||
char *name;
|
||||
enum hanwang_tablet_type type;
|
||||
int pkg_len;
|
||||
int max_x;
|
||||
int max_y;
|
||||
int max_tilt_x;
|
||||
int max_tilt_y;
|
||||
int max_pressure;
|
||||
};
|
||||
|
||||
static const struct hanwang_features features_array[] = {
|
||||
{ 0x8528, "Hanwang Art Master III 0906", HANWANG_ART_MASTER_III,
|
||||
ART_MASTER_PKGLEN_MAX, 0x5757, 0x3692, 0x3f, 0x7f, 2048 },
|
||||
{ 0x8529, "Hanwang Art Master III 0604", HANWANG_ART_MASTER_III,
|
||||
ART_MASTER_PKGLEN_MAX, 0x3d84, 0x2672, 0x3f, 0x7f, 2048 },
|
||||
{ 0x852a, "Hanwang Art Master III 1308", HANWANG_ART_MASTER_III,
|
||||
ART_MASTER_PKGLEN_MAX, 0x7f00, 0x4f60, 0x3f, 0x7f, 2048 },
|
||||
{ 0x8401, "Hanwang Art Master HD 5012", HANWANG_ART_MASTER_HD,
|
||||
ART_MASTER_PKGLEN_MAX, 0x678e, 0x4150, 0x3f, 0x7f, 1024 },
|
||||
};
|
||||
|
||||
static const int hw_eventtypes[] = {
|
||||
EV_KEY, EV_ABS, EV_MSC,
|
||||
};
|
||||
|
||||
static const int hw_absevents[] = {
|
||||
ABS_X, ABS_Y, ABS_TILT_X, ABS_TILT_Y, ABS_WHEEL,
|
||||
ABS_RX, ABS_RY, ABS_PRESSURE, ABS_MISC,
|
||||
};
|
||||
|
||||
static const int hw_btnevents[] = {
|
||||
BTN_STYLUS, BTN_STYLUS2, BTN_TOOL_PEN, BTN_TOOL_RUBBER,
|
||||
BTN_TOOL_MOUSE, BTN_TOOL_FINGER,
|
||||
BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8,
|
||||
};
|
||||
|
||||
static const int hw_mscevents[] = {
|
||||
MSC_SERIAL,
|
||||
};
|
||||
|
||||
static void hanwang_parse_packet(struct hanwang *hanwang)
|
||||
{
|
||||
unsigned char *data = hanwang->data;
|
||||
struct input_dev *input_dev = hanwang->dev;
|
||||
struct usb_device *dev = hanwang->usbdev;
|
||||
enum hanwang_tablet_type type = hanwang->features->type;
|
||||
int i;
|
||||
u16 x, y, p;
|
||||
|
||||
switch (data[0]) {
|
||||
case 0x02: /* data packet */
|
||||
switch (data[1]) {
|
||||
case 0x80: /* tool prox out */
|
||||
hanwang->current_id = 0;
|
||||
input_report_key(input_dev, hanwang->current_tool, 0);
|
||||
break;
|
||||
|
||||
case 0xc2: /* first time tool prox in */
|
||||
switch (data[3] & 0xf0) {
|
||||
case 0x20: /* art_master III */
|
||||
case 0x30: /* art_master_HD */
|
||||
hanwang->current_id = STYLUS_DEVICE_ID;
|
||||
hanwang->current_tool = BTN_TOOL_PEN;
|
||||
input_report_key(input_dev, BTN_TOOL_PEN, 1);
|
||||
break;
|
||||
case 0xa0: /* art_master III */
|
||||
case 0xb0: /* art_master_HD */
|
||||
hanwang->current_id = ERASER_DEVICE_ID;
|
||||
hanwang->current_tool = BTN_TOOL_RUBBER;
|
||||
input_report_key(input_dev, BTN_TOOL_RUBBER, 1);
|
||||
break;
|
||||
default:
|
||||
hanwang->current_id = 0;
|
||||
dev_dbg(&dev->dev,
|
||||
"unknown tablet tool %02x ", data[0]);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default: /* tool data packet */
|
||||
x = (data[2] << 8) | data[3];
|
||||
y = (data[4] << 8) | data[5];
|
||||
|
||||
switch (type) {
|
||||
case HANWANG_ART_MASTER_III:
|
||||
p = (data[6] << 3) |
|
||||
((data[7] & 0xc0) >> 5) |
|
||||
(data[1] & 0x01);
|
||||
break;
|
||||
|
||||
case HANWANG_ART_MASTER_HD:
|
||||
p = (data[7] >> 6) | (data[6] << 2);
|
||||
break;
|
||||
|
||||
default:
|
||||
p = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
input_report_abs(input_dev, ABS_X,
|
||||
le16_to_cpup((__le16 *)&x));
|
||||
input_report_abs(input_dev, ABS_Y,
|
||||
le16_to_cpup((__le16 *)&y));
|
||||
input_report_abs(input_dev, ABS_PRESSURE,
|
||||
le16_to_cpup((__le16 *)&p));
|
||||
input_report_abs(input_dev, ABS_TILT_X, data[7] & 0x3f);
|
||||
input_report_abs(input_dev, ABS_TILT_Y, data[8] & 0x7f);
|
||||
input_report_key(input_dev, BTN_STYLUS, data[1] & 0x02);
|
||||
input_report_key(input_dev, BTN_STYLUS2, data[1] & 0x04);
|
||||
break;
|
||||
}
|
||||
input_report_abs(input_dev, ABS_MISC, hanwang->current_id);
|
||||
input_event(input_dev, EV_MSC, MSC_SERIAL,
|
||||
hanwang->features->pid);
|
||||
break;
|
||||
|
||||
case 0x0c:
|
||||
/* roll wheel */
|
||||
hanwang->current_id = PAD_DEVICE_ID;
|
||||
|
||||
switch (type) {
|
||||
case HANWANG_ART_MASTER_III:
|
||||
input_report_key(input_dev, BTN_TOOL_FINGER, data[1] ||
|
||||
data[2] || data[3]);
|
||||
input_report_abs(input_dev, ABS_WHEEL, data[1]);
|
||||
input_report_key(input_dev, BTN_0, data[2]);
|
||||
for (i = 0; i < 8; i++)
|
||||
input_report_key(input_dev,
|
||||
BTN_1 + i, data[3] & (1 << i));
|
||||
break;
|
||||
|
||||
case HANWANG_ART_MASTER_HD:
|
||||
input_report_key(input_dev, BTN_TOOL_FINGER, data[1] ||
|
||||
data[2] || data[3] || data[4] ||
|
||||
data[5] || data[6]);
|
||||
input_report_abs(input_dev, ABS_RX,
|
||||
((data[1] & 0x1f) << 8) | data[2]);
|
||||
input_report_abs(input_dev, ABS_RY,
|
||||
((data[3] & 0x1f) << 8) | data[4]);
|
||||
input_report_key(input_dev, BTN_0, data[5] & 0x01);
|
||||
for (i = 0; i < 4; i++) {
|
||||
input_report_key(input_dev,
|
||||
BTN_1 + i, data[5] & (1 << i));
|
||||
input_report_key(input_dev,
|
||||
BTN_5 + i, data[6] & (1 << i));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
input_report_abs(input_dev, ABS_MISC, hanwang->current_id);
|
||||
input_event(input_dev, EV_MSC, MSC_SERIAL, 0xffffffff);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_dbg(&dev->dev, "error packet %02x ", data[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
input_sync(input_dev);
|
||||
}
|
||||
|
||||
static void hanwang_irq(struct urb *urb)
|
||||
{
|
||||
struct hanwang *hanwang = urb->context;
|
||||
struct usb_device *dev = hanwang->usbdev;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */;
|
||||
hanwang_parse_packet(hanwang);
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dev_err(&dev->dev, "%s - urb shutting down with status: %d",
|
||||
__func__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dev_err(&dev->dev, "%s - nonzero urb status received: %d",
|
||||
__func__, urb->status);
|
||||
break;
|
||||
}
|
||||
|
||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
dev_err(&dev->dev, "%s - usb_submit_urb failed with result %d",
|
||||
__func__, retval);
|
||||
}
|
||||
|
||||
static int hanwang_open(struct input_dev *dev)
|
||||
{
|
||||
struct hanwang *hanwang = input_get_drvdata(dev);
|
||||
|
||||
hanwang->irq->dev = hanwang->usbdev;
|
||||
if (usb_submit_urb(hanwang->irq, GFP_KERNEL))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hanwang_close(struct input_dev *dev)
|
||||
{
|
||||
struct hanwang *hanwang = input_get_drvdata(dev);
|
||||
|
||||
usb_kill_urb(hanwang->irq);
|
||||
}
|
||||
|
||||
static bool get_features(struct usb_device *dev, struct hanwang *hanwang)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(features_array); i++) {
|
||||
if (le16_to_cpu(dev->descriptor.idProduct) ==
|
||||
features_array[i].pid) {
|
||||
hanwang->features = &features_array[i];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static int hanwang_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct hanwang *hanwang;
|
||||
struct input_dev *input_dev;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
hanwang = kzalloc(sizeof(struct hanwang), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!hanwang || !input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
if (!get_features(dev, hanwang)) {
|
||||
error = -ENXIO;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
hanwang->data = usb_alloc_coherent(dev, hanwang->features->pkg_len,
|
||||
GFP_KERNEL, &hanwang->data_dma);
|
||||
if (!hanwang->data) {
|
||||
error = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
hanwang->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!hanwang->irq) {
|
||||
error = -ENOMEM;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
hanwang->usbdev = dev;
|
||||
hanwang->dev = input_dev;
|
||||
|
||||
usb_make_path(dev, hanwang->phys, sizeof(hanwang->phys));
|
||||
strlcat(hanwang->phys, "/input0", sizeof(hanwang->phys));
|
||||
|
||||
strlcpy(hanwang->name, hanwang->features->name, sizeof(hanwang->name));
|
||||
input_dev->name = hanwang->name;
|
||||
input_dev->phys = hanwang->phys;
|
||||
usb_to_input_id(dev, &input_dev->id);
|
||||
input_dev->dev.parent = &intf->dev;
|
||||
|
||||
input_set_drvdata(input_dev, hanwang);
|
||||
|
||||
input_dev->open = hanwang_open;
|
||||
input_dev->close = hanwang_close;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hw_eventtypes); ++i)
|
||||
__set_bit(hw_eventtypes[i], input_dev->evbit);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hw_absevents); ++i)
|
||||
__set_bit(hw_absevents[i], input_dev->absbit);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hw_btnevents); ++i)
|
||||
__set_bit(hw_btnevents[i], input_dev->keybit);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hw_mscevents); ++i)
|
||||
__set_bit(hw_mscevents[i], input_dev->mscbit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X,
|
||||
0, hanwang->features->max_x, 4, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y,
|
||||
0, hanwang->features->max_y, 4, 0);
|
||||
input_set_abs_params(input_dev, ABS_TILT_X,
|
||||
0, hanwang->features->max_tilt_x, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_TILT_Y,
|
||||
0, hanwang->features->max_tilt_y, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE,
|
||||
0, hanwang->features->max_pressure, 0, 0);
|
||||
|
||||
endpoint = &intf->cur_altsetting->endpoint[0].desc;
|
||||
usb_fill_int_urb(hanwang->irq, dev,
|
||||
usb_rcvintpipe(dev, endpoint->bEndpointAddress),
|
||||
hanwang->data, hanwang->features->pkg_len,
|
||||
hanwang_irq, hanwang, endpoint->bInterval);
|
||||
hanwang->irq->transfer_dma = hanwang->data_dma;
|
||||
hanwang->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
error = input_register_device(hanwang->dev);
|
||||
if (error)
|
||||
goto fail3;
|
||||
|
||||
usb_set_intfdata(intf, hanwang);
|
||||
|
||||
return 0;
|
||||
|
||||
fail3: usb_free_urb(hanwang->irq);
|
||||
fail2: usb_free_coherent(dev, hanwang->features->pkg_len,
|
||||
hanwang->data, hanwang->data_dma);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(hanwang);
|
||||
return error;
|
||||
|
||||
}
|
||||
|
||||
static void hanwang_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct hanwang *hanwang = usb_get_intfdata(intf);
|
||||
|
||||
input_unregister_device(hanwang->dev);
|
||||
usb_free_urb(hanwang->irq);
|
||||
usb_free_coherent(interface_to_usbdev(intf),
|
||||
hanwang->features->pkg_len, hanwang->data,
|
||||
hanwang->data_dma);
|
||||
kfree(hanwang);
|
||||
usb_set_intfdata(intf, NULL);
|
||||
}
|
||||
|
||||
static const struct usb_device_id hanwang_ids[] = {
|
||||
{ HANWANG_TABLET_DEVICE(USB_VENDOR_ID_HANWANG, HANWANG_TABLET_INT_CLASS,
|
||||
HANWANG_TABLET_INT_SUB_CLASS, HANWANG_TABLET_INT_PROTOCOL) },
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, hanwang_ids);
|
||||
|
||||
static struct usb_driver hanwang_driver = {
|
||||
.name = "hanwang",
|
||||
.probe = hanwang_probe,
|
||||
.disconnect = hanwang_disconnect,
|
||||
.id_table = hanwang_ids,
|
||||
};
|
||||
|
||||
static int __init hanwang_init(void)
|
||||
{
|
||||
return usb_register(&hanwang_driver);
|
||||
}
|
||||
|
||||
static void __exit hanwang_exit(void)
|
||||
{
|
||||
usb_deregister(&hanwang_driver);
|
||||
}
|
||||
|
||||
module_init(hanwang_init);
|
||||
module_exit(hanwang_exit);
|
@ -118,6 +118,7 @@ struct wacom {
|
||||
extern const struct usb_device_id wacom_ids[];
|
||||
|
||||
void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
|
||||
void wacom_setup_device_quirks(struct wacom_features *features);
|
||||
void wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||
struct wacom_wac *wacom_wac);
|
||||
#endif
|
||||
|
@ -120,14 +120,16 @@ static int wacom_open(struct input_dev *dev)
|
||||
|
||||
out:
|
||||
mutex_unlock(&wacom->lock);
|
||||
if (retval)
|
||||
usb_autopm_put_interface(wacom->intf);
|
||||
usb_autopm_put_interface(wacom->intf);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void wacom_close(struct input_dev *dev)
|
||||
{
|
||||
struct wacom *wacom = input_get_drvdata(dev);
|
||||
int autopm_error;
|
||||
|
||||
autopm_error = usb_autopm_get_interface(wacom->intf);
|
||||
|
||||
mutex_lock(&wacom->lock);
|
||||
usb_kill_urb(wacom->irq);
|
||||
@ -135,7 +137,8 @@ static void wacom_close(struct input_dev *dev)
|
||||
wacom->intf->needs_remote_wakeup = 0;
|
||||
mutex_unlock(&wacom->lock);
|
||||
|
||||
usb_autopm_put_interface(wacom->intf);
|
||||
if (!autopm_error)
|
||||
usb_autopm_put_interface(wacom->intf);
|
||||
}
|
||||
|
||||
static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hid_desc,
|
||||
@ -196,17 +199,30 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
|
||||
features->pktlen = WACOM_PKGLEN_TPC2FG;
|
||||
features->device_type = BTN_TOOL_TRIPLETAP;
|
||||
}
|
||||
features->x_max =
|
||||
get_unaligned_le16(&report[i + 3]);
|
||||
features->x_phy =
|
||||
get_unaligned_le16(&report[i + 6]);
|
||||
features->unit = report[i + 9];
|
||||
features->unitExpo = report[i + 11];
|
||||
i += 12;
|
||||
if (features->type == BAMBOO_PT) {
|
||||
/* need to reset back */
|
||||
features->pktlen = WACOM_PKGLEN_BBTOUCH;
|
||||
features->device_type = BTN_TOOL_TRIPLETAP;
|
||||
features->x_phy =
|
||||
get_unaligned_le16(&report[i + 5]);
|
||||
features->x_max =
|
||||
get_unaligned_le16(&report[i + 8]);
|
||||
i += 15;
|
||||
} else {
|
||||
features->x_max =
|
||||
get_unaligned_le16(&report[i + 3]);
|
||||
features->x_phy =
|
||||
get_unaligned_le16(&report[i + 6]);
|
||||
features->unit = report[i + 9];
|
||||
features->unitExpo = report[i + 11];
|
||||
i += 12;
|
||||
}
|
||||
} else if (pen) {
|
||||
/* penabled only accepts exact bytes of data */
|
||||
if (features->type == TABLETPC2FG)
|
||||
features->pktlen = WACOM_PKGLEN_GRAPHIRE;
|
||||
if (features->type == BAMBOO_PT)
|
||||
features->pktlen = WACOM_PKGLEN_BBFUN;
|
||||
features->device_type = BTN_TOOL_PEN;
|
||||
features->x_max =
|
||||
get_unaligned_le16(&report[i + 3]);
|
||||
@ -235,6 +251,15 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
|
||||
features->y_phy =
|
||||
get_unaligned_le16(&report[i + 6]);
|
||||
i += 7;
|
||||
} else if (features->type == BAMBOO_PT) {
|
||||
/* need to reset back */
|
||||
features->pktlen = WACOM_PKGLEN_BBTOUCH;
|
||||
features->device_type = BTN_TOOL_TRIPLETAP;
|
||||
features->y_phy =
|
||||
get_unaligned_le16(&report[i + 3]);
|
||||
features->y_max =
|
||||
get_unaligned_le16(&report[i + 6]);
|
||||
i += 12;
|
||||
} else {
|
||||
features->y_max =
|
||||
features->x_max;
|
||||
@ -246,6 +271,8 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
|
||||
/* penabled only accepts exact bytes of data */
|
||||
if (features->type == TABLETPC2FG)
|
||||
features->pktlen = WACOM_PKGLEN_GRAPHIRE;
|
||||
if (features->type == BAMBOO_PT)
|
||||
features->pktlen = WACOM_PKGLEN_BBFUN;
|
||||
features->device_type = BTN_TOOL_PEN;
|
||||
features->y_max =
|
||||
get_unaligned_le16(&report[i + 3]);
|
||||
@ -296,8 +323,9 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
|
||||
if (!rep_data)
|
||||
return error;
|
||||
|
||||
/* ask to report tablet data if it is 2FGT or not a Tablet PC */
|
||||
if (features->device_type == BTN_TOOL_TRIPLETAP) {
|
||||
/* ask to report tablet data if it is 2FGT Tablet PC or
|
||||
* not a Tablet PC */
|
||||
if (features->type == TABLETPC2FG) {
|
||||
do {
|
||||
rep_data[0] = 3;
|
||||
rep_data[1] = 4;
|
||||
@ -309,7 +337,7 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
|
||||
WAC_HID_FEATURE_REPORT, report_id,
|
||||
rep_data, 3);
|
||||
} while ((error < 0 || rep_data[1] != 4) && limit++ < 5);
|
||||
} else if (features->type != TABLETPC && features->type != TABLETPC2FG) {
|
||||
} else if (features->type != TABLETPC) {
|
||||
do {
|
||||
rep_data[0] = 2;
|
||||
rep_data[1] = 2;
|
||||
@ -334,11 +362,16 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
|
||||
struct usb_host_interface *interface = intf->cur_altsetting;
|
||||
struct hid_descriptor *hid_desc;
|
||||
|
||||
/* default device to penabled */
|
||||
/* default features */
|
||||
features->device_type = BTN_TOOL_PEN;
|
||||
features->x_fuzz = 4;
|
||||
features->y_fuzz = 4;
|
||||
features->pressure_fuzz = 0;
|
||||
features->distance_fuzz = 0;
|
||||
|
||||
/* only Tablet PCs need to retrieve the info */
|
||||
if ((features->type != TABLETPC) && (features->type != TABLETPC2FG))
|
||||
/* only Tablet PCs and Bamboo P&T need to retrieve the info */
|
||||
if ((features->type != TABLETPC) && (features->type != TABLETPC2FG) &&
|
||||
(features->type != BAMBOO_PT))
|
||||
goto out;
|
||||
|
||||
if (usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc)) {
|
||||
@ -353,12 +386,6 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
/* touch device found but size is not defined. use default */
|
||||
if (features->device_type == BTN_TOOL_DOUBLETAP && !features->x_max) {
|
||||
features->x_max = 1023;
|
||||
features->y_max = 1023;
|
||||
}
|
||||
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
@ -494,9 +521,11 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
|
||||
if (error)
|
||||
goto fail2;
|
||||
|
||||
wacom_setup_device_quirks(features);
|
||||
|
||||
strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name));
|
||||
|
||||
if (features->type == TABLETPC || features->type == TABLETPC2FG) {
|
||||
if (features->quirks & WACOM_QUIRK_MULTI_INPUT) {
|
||||
/* Append the device type to the name */
|
||||
strlcat(wacom_wac->name,
|
||||
features->device_type == BTN_TOOL_PEN ?
|
||||
|
@ -857,6 +857,134 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int wacom_bpt_touch(struct wacom_wac *wacom)
|
||||
{
|
||||
struct wacom_features *features = &wacom->features;
|
||||
struct input_dev *input = wacom->input;
|
||||
unsigned char *data = wacom->data;
|
||||
int sp = 0, sx = 0, sy = 0, count = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
int p = data[9 * i + 2];
|
||||
input_mt_slot(input, i);
|
||||
/*
|
||||
* Touch events need to be disabled while stylus is
|
||||
* in proximity because user's hand is resting on touchpad
|
||||
* and sending unwanted events. User expects tablet buttons
|
||||
* to continue working though.
|
||||
*/
|
||||
if (p && !wacom->shared->stylus_in_proximity) {
|
||||
int x = get_unaligned_be16(&data[9 * i + 3]) & 0x7ff;
|
||||
int y = get_unaligned_be16(&data[9 * i + 5]) & 0x7ff;
|
||||
if (features->quirks & WACOM_QUIRK_BBTOUCH_LOWRES) {
|
||||
x <<= 5;
|
||||
y <<= 5;
|
||||
}
|
||||
input_report_abs(input, ABS_MT_PRESSURE, p);
|
||||
input_report_abs(input, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(input, ABS_MT_POSITION_Y, y);
|
||||
if (wacom->id[i] < 0)
|
||||
wacom->id[i] = wacom->trk_id++ & MAX_TRACKING_ID;
|
||||
if (!count++)
|
||||
sp = p, sx = x, sy = y;
|
||||
} else {
|
||||
wacom->id[i] = -1;
|
||||
}
|
||||
input_report_abs(input, ABS_MT_TRACKING_ID, wacom->id[i]);
|
||||
}
|
||||
|
||||
input_report_key(input, BTN_TOUCH, count > 0);
|
||||
input_report_key(input, BTN_TOOL_FINGER, count == 1);
|
||||
input_report_key(input, BTN_TOOL_DOUBLETAP, count == 2);
|
||||
|
||||
input_report_abs(input, ABS_PRESSURE, sp);
|
||||
input_report_abs(input, ABS_X, sx);
|
||||
input_report_abs(input, ABS_Y, sy);
|
||||
|
||||
input_report_key(input, BTN_LEFT, (data[1] & 0x08) != 0);
|
||||
input_report_key(input, BTN_FORWARD, (data[1] & 0x04) != 0);
|
||||
input_report_key(input, BTN_BACK, (data[1] & 0x02) != 0);
|
||||
input_report_key(input, BTN_RIGHT, (data[1] & 0x01) != 0);
|
||||
|
||||
input_sync(input);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wacom_bpt_pen(struct wacom_wac *wacom)
|
||||
{
|
||||
struct input_dev *input = wacom->input;
|
||||
unsigned char *data = wacom->data;
|
||||
int prox = 0, x = 0, y = 0, p = 0, d = 0, pen = 0, btn1 = 0, btn2 = 0;
|
||||
|
||||
/*
|
||||
* Similar to Graphire protocol, data[1] & 0x20 is proximity and
|
||||
* data[1] & 0x18 is tool ID. 0x30 is safety check to ignore
|
||||
* 2 unused tool ID's.
|
||||
*/
|
||||
prox = (data[1] & 0x30) == 0x30;
|
||||
|
||||
/*
|
||||
* All reports shared between PEN and RUBBER tool must be
|
||||
* forced to a known starting value (zero) when transitioning to
|
||||
* out-of-prox.
|
||||
*
|
||||
* If not reset then, to userspace, it will look like lost events
|
||||
* if new tool comes in-prox with same values as previous tool sent.
|
||||
*
|
||||
* Hardware does report zero in most out-of-prox cases but not all.
|
||||
*/
|
||||
if (prox) {
|
||||
if (!wacom->shared->stylus_in_proximity) {
|
||||
if (data[1] & 0x08) {
|
||||
wacom->tool[0] = BTN_TOOL_RUBBER;
|
||||
wacom->id[0] = ERASER_DEVICE_ID;
|
||||
} else {
|
||||
wacom->tool[0] = BTN_TOOL_PEN;
|
||||
wacom->id[0] = STYLUS_DEVICE_ID;
|
||||
}
|
||||
wacom->shared->stylus_in_proximity = true;
|
||||
}
|
||||
x = le16_to_cpup((__le16 *)&data[2]);
|
||||
y = le16_to_cpup((__le16 *)&data[4]);
|
||||
p = le16_to_cpup((__le16 *)&data[6]);
|
||||
d = data[8];
|
||||
pen = data[1] & 0x01;
|
||||
btn1 = data[1] & 0x02;
|
||||
btn2 = data[1] & 0x04;
|
||||
}
|
||||
|
||||
input_report_key(input, BTN_TOUCH, pen);
|
||||
input_report_key(input, BTN_STYLUS, btn1);
|
||||
input_report_key(input, BTN_STYLUS2, btn2);
|
||||
|
||||
input_report_abs(input, ABS_X, x);
|
||||
input_report_abs(input, ABS_Y, y);
|
||||
input_report_abs(input, ABS_PRESSURE, p);
|
||||
input_report_abs(input, ABS_DISTANCE, d);
|
||||
|
||||
if (!prox) {
|
||||
wacom->id[0] = 0;
|
||||
wacom->shared->stylus_in_proximity = false;
|
||||
}
|
||||
|
||||
input_report_key(input, wacom->tool[0], prox); /* PEN or RUBBER */
|
||||
input_report_abs(input, ABS_MISC, wacom->id[0]); /* TOOL ID */
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len)
|
||||
{
|
||||
if (len == WACOM_PKGLEN_BBTOUCH)
|
||||
return wacom_bpt_touch(wacom);
|
||||
else if (len == WACOM_PKGLEN_BBFUN)
|
||||
return wacom_bpt_pen(wacom);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
|
||||
{
|
||||
bool sync;
|
||||
@ -902,6 +1030,10 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
|
||||
sync = wacom_tpc_irq(wacom_wac, len);
|
||||
break;
|
||||
|
||||
case BAMBOO_PT:
|
||||
sync = wacom_bpt_irq(wacom_wac, len);
|
||||
break;
|
||||
|
||||
default:
|
||||
sync = false;
|
||||
break;
|
||||
@ -911,26 +1043,17 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
|
||||
input_sync(wacom_wac->input);
|
||||
}
|
||||
|
||||
static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
|
||||
static void wacom_setup_cintiq(struct wacom_wac *wacom_wac)
|
||||
{
|
||||
struct input_dev *input_dev = wacom_wac->input;
|
||||
|
||||
input_set_capability(input_dev, EV_MSC, MSC_SERIAL);
|
||||
input_set_capability(input_dev, EV_REL, REL_WHEEL);
|
||||
|
||||
__set_bit(BTN_LEFT, input_dev->keybit);
|
||||
__set_bit(BTN_RIGHT, input_dev->keybit);
|
||||
__set_bit(BTN_MIDDLE, input_dev->keybit);
|
||||
__set_bit(BTN_SIDE, input_dev->keybit);
|
||||
__set_bit(BTN_EXTRA, input_dev->keybit);
|
||||
|
||||
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_PEN, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_BRUSH, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_PENCIL, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_AIRBRUSH, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_LENS, input_dev->keybit);
|
||||
__set_bit(BTN_STYLUS, input_dev->keybit);
|
||||
__set_bit(BTN_STYLUS2, input_dev->keybit);
|
||||
|
||||
@ -939,10 +1062,55 @@ static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
|
||||
input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0);
|
||||
}
|
||||
|
||||
static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
|
||||
{
|
||||
struct input_dev *input_dev = wacom_wac->input;
|
||||
|
||||
input_set_capability(input_dev, EV_REL, REL_WHEEL);
|
||||
|
||||
wacom_setup_cintiq(wacom_wac);
|
||||
|
||||
__set_bit(BTN_LEFT, input_dev->keybit);
|
||||
__set_bit(BTN_RIGHT, input_dev->keybit);
|
||||
__set_bit(BTN_MIDDLE, input_dev->keybit);
|
||||
__set_bit(BTN_SIDE, input_dev->keybit);
|
||||
__set_bit(BTN_EXTRA, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_LENS, input_dev->keybit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
|
||||
}
|
||||
|
||||
void wacom_setup_device_quirks(struct wacom_features *features)
|
||||
{
|
||||
|
||||
/* touch device found but size is not defined. use default */
|
||||
if (features->device_type == BTN_TOOL_DOUBLETAP && !features->x_max) {
|
||||
features->x_max = 1023;
|
||||
features->y_max = 1023;
|
||||
}
|
||||
|
||||
/* these device have multiple inputs */
|
||||
if (features->type == TABLETPC || features->type == TABLETPC2FG ||
|
||||
features->type == BAMBOO_PT)
|
||||
features->quirks |= WACOM_QUIRK_MULTI_INPUT;
|
||||
|
||||
/* quirks for bamboo touch */
|
||||
if (features->type == BAMBOO_PT &&
|
||||
features->device_type == BTN_TOOL_TRIPLETAP) {
|
||||
features->x_max <<= 5;
|
||||
features->y_max <<= 5;
|
||||
features->x_fuzz <<= 5;
|
||||
features->y_fuzz <<= 5;
|
||||
features->pressure_max = 256;
|
||||
features->pressure_fuzz = 16;
|
||||
features->quirks |= WACOM_QUIRK_BBTOUCH_LOWRES;
|
||||
}
|
||||
}
|
||||
|
||||
void wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||
struct wacom_wac *wacom_wac)
|
||||
{
|
||||
@ -953,9 +1121,12 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||
|
||||
__set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X, 0, features->x_max, 4, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, features->y_max, 4, 0);
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_X, 0, features->x_max,
|
||||
features->x_fuzz, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, features->y_max,
|
||||
features->y_fuzz, 0);
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max,
|
||||
features->pressure_fuzz, 0);
|
||||
|
||||
__set_bit(ABS_MISC, input_dev->absbit);
|
||||
|
||||
@ -1005,9 +1176,19 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||
__set_bit(BTN_9, input_dev->keybit);
|
||||
/* fall through */
|
||||
|
||||
case CINTIQ:
|
||||
for (i = 0; i < 8; i++)
|
||||
__set_bit(BTN_0 + i, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
|
||||
wacom_setup_cintiq(wacom_wac);
|
||||
break;
|
||||
|
||||
case INTUOS3:
|
||||
case INTUOS3L:
|
||||
case CINTIQ:
|
||||
__set_bit(BTN_4, input_dev->keybit);
|
||||
__set_bit(BTN_5, input_dev->keybit);
|
||||
__set_bit(BTN_6, input_dev->keybit);
|
||||
@ -1078,6 +1259,38 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||
case PENPARTNER:
|
||||
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
|
||||
break;
|
||||
|
||||
case BAMBOO_PT:
|
||||
__clear_bit(ABS_MISC, input_dev->absbit);
|
||||
|
||||
if (features->device_type == BTN_TOOL_TRIPLETAP) {
|
||||
__set_bit(BTN_LEFT, input_dev->keybit);
|
||||
__set_bit(BTN_FORWARD, input_dev->keybit);
|
||||
__set_bit(BTN_BACK, input_dev->keybit);
|
||||
__set_bit(BTN_RIGHT, input_dev->keybit);
|
||||
|
||||
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
|
||||
|
||||
input_mt_create_slots(input_dev, 2);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
|
||||
0, features->x_max,
|
||||
features->x_fuzz, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
|
||||
0, features->y_max,
|
||||
features->y_fuzz, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_PRESSURE,
|
||||
0, features->pressure_max,
|
||||
features->pressure_fuzz, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0,
|
||||
MAX_TRACKING_ID, 0, 0);
|
||||
} else if (features->device_type == BTN_TOOL_PEN) {
|
||||
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_PEN, input_dev->keybit);
|
||||
__set_bit(BTN_STYLUS, input_dev->keybit);
|
||||
__set_bit(BTN_STYLUS2, input_dev->keybit);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1215,6 +1428,14 @@ static const struct wacom_features wacom_features_0xE3 =
|
||||
{ "Wacom ISDv4 E3", WACOM_PKGLEN_TPC2FG, 26202, 16325, 255, 0, TABLETPC2FG };
|
||||
static const struct wacom_features wacom_features_0x47 =
|
||||
{ "Wacom Intuos2 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, 31, INTUOS };
|
||||
static struct wacom_features wacom_features_0xD0 =
|
||||
{ "Wacom Bamboo 2FG", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT };
|
||||
static struct wacom_features wacom_features_0xD1 =
|
||||
{ "Wacom Bamboo 2FG 4x5", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT };
|
||||
static struct wacom_features wacom_features_0xD2 =
|
||||
{ "Wacom Bamboo Craft", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT };
|
||||
static struct wacom_features wacom_features_0xD3 =
|
||||
{ "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT };
|
||||
|
||||
#define USB_DEVICE_WACOM(prod) \
|
||||
USB_DEVICE(USB_VENDOR_ID_WACOM, prod), \
|
||||
@ -1279,6 +1500,10 @@ const struct usb_device_id wacom_ids[] = {
|
||||
{ USB_DEVICE_WACOM(0xC6) },
|
||||
{ USB_DEVICE_WACOM(0xC7) },
|
||||
{ USB_DEVICE_WACOM(0xCE) },
|
||||
{ USB_DEVICE_WACOM(0xD0) },
|
||||
{ USB_DEVICE_WACOM(0xD1) },
|
||||
{ USB_DEVICE_WACOM(0xD2) },
|
||||
{ USB_DEVICE_WACOM(0xD3) },
|
||||
{ USB_DEVICE_WACOM(0xF0) },
|
||||
{ USB_DEVICE_WACOM(0xCC) },
|
||||
{ USB_DEVICE_WACOM(0x90) },
|
||||
|
@ -21,6 +21,7 @@
|
||||
#define WACOM_PKGLEN_INTUOS 10
|
||||
#define WACOM_PKGLEN_TPC1FG 5
|
||||
#define WACOM_PKGLEN_TPC2FG 14
|
||||
#define WACOM_PKGLEN_BBTOUCH 20
|
||||
|
||||
/* device IDs */
|
||||
#define STYLUS_DEVICE_ID 0x02
|
||||
@ -37,6 +38,13 @@
|
||||
#define WACOM_REPORT_TPC1FG 6
|
||||
#define WACOM_REPORT_TPC2FG 13
|
||||
|
||||
/* device quirks */
|
||||
#define WACOM_QUIRK_MULTI_INPUT 0x0001
|
||||
#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002
|
||||
|
||||
/* largest reported tracking id */
|
||||
#define MAX_TRACKING_ID 0xfff
|
||||
|
||||
enum {
|
||||
PENPARTNER = 0,
|
||||
GRAPHIRE,
|
||||
@ -44,6 +52,7 @@ enum {
|
||||
PTU,
|
||||
PL,
|
||||
DTU,
|
||||
BAMBOO_PT,
|
||||
INTUOS,
|
||||
INTUOS3S,
|
||||
INTUOS3,
|
||||
@ -73,6 +82,11 @@ struct wacom_features {
|
||||
int y_phy;
|
||||
unsigned char unit;
|
||||
unsigned char unitExpo;
|
||||
int x_fuzz;
|
||||
int y_fuzz;
|
||||
int pressure_fuzz;
|
||||
int distance_fuzz;
|
||||
unsigned quirks;
|
||||
};
|
||||
|
||||
struct wacom_shared {
|
||||
@ -86,6 +100,7 @@ struct wacom_wac {
|
||||
int id[3];
|
||||
__u32 serial[2];
|
||||
int last_finger;
|
||||
int trk_id;
|
||||
struct wacom_features features;
|
||||
struct wacom_shared *shared;
|
||||
struct input_dev *input;
|
||||
|
@ -98,6 +98,18 @@ config TOUCHSCREEN_BITSY
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called h3600_ts_input.
|
||||
|
||||
config TOUCHSCREEN_BU21013
|
||||
tristate "BU21013 based touch panel controllers"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you have a bu21013 touchscreen connected to
|
||||
your system.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called bu21013_ts.
|
||||
|
||||
config TOUCHSCREEN_CY8CTMG110
|
||||
tristate "cy8ctmg110 touchscreen"
|
||||
depends on I2C
|
||||
@ -214,6 +226,16 @@ config TOUCHSCREEN_WACOM_W8001
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called wacom_w8001.
|
||||
|
||||
config TOUCHSCREEN_LPC32XX
|
||||
tristate "LPC32XX touchscreen controller"
|
||||
depends on ARCH_LPC32XX
|
||||
help
|
||||
Say Y here if you have a LPC32XX device and want
|
||||
to support the built-in touchscreen.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called lpc32xx_ts.
|
||||
|
||||
config TOUCHSCREEN_MCS5000
|
||||
tristate "MELFAS MCS-5000 touchscreen"
|
||||
depends on I2C
|
||||
@ -250,6 +272,18 @@ config TOUCHSCREEN_INEXIO
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called inexio.
|
||||
|
||||
config TOUCHSCREEN_INTEL_MID
|
||||
tristate "Intel MID platform resistive touchscreen"
|
||||
depends on INTEL_SCU_IPC
|
||||
help
|
||||
Say Y here if you have a Intel MID based touchscreen in
|
||||
your system.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called intel_mid_touch.
|
||||
|
||||
config TOUCHSCREEN_MK712
|
||||
tristate "ICS MicroClock MK712 touchscreen"
|
||||
help
|
||||
|
@ -14,6 +14,7 @@ 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_BITSY) += h3600_ts_input.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o
|
||||
@ -23,6 +24,8 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o
|
||||
|
@ -191,13 +191,12 @@ struct ad7877 {
|
||||
struct spi_message msg;
|
||||
|
||||
struct mutex mutex;
|
||||
unsigned disabled:1; /* P: mutex */
|
||||
unsigned gpio3:1; /* P: mutex */
|
||||
unsigned gpio4:1; /* P: mutex */
|
||||
bool disabled; /* P: mutex */
|
||||
bool gpio3; /* P: mutex */
|
||||
bool gpio4; /* P: mutex */
|
||||
|
||||
spinlock_t lock;
|
||||
struct timer_list timer; /* P: lock */
|
||||
unsigned pending:1; /* P: lock */
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
@ -206,8 +205,8 @@ struct ad7877 {
|
||||
u16 conversion_data[AD7877_NR_SENSE] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int gpio3;
|
||||
module_param(gpio3, int, 0);
|
||||
static bool gpio3;
|
||||
module_param(gpio3, bool, 0);
|
||||
MODULE_PARM_DESC(gpio3, "If gpio3 is set to 1 AUX3 acts as GPIO3");
|
||||
|
||||
/*
|
||||
@ -230,6 +229,7 @@ static int ad7877_read(struct spi_device *spi, u16 reg)
|
||||
AD7877_READADD(reg));
|
||||
req->xfer[0].tx_buf = &req->command;
|
||||
req->xfer[0].len = 2;
|
||||
req->xfer[0].cs_change = 1;
|
||||
|
||||
req->xfer[1].rx_buf = &req->sample;
|
||||
req->xfer[1].len = 2;
|
||||
@ -295,20 +295,25 @@ static int ad7877_read_adc(struct spi_device *spi, unsigned command)
|
||||
|
||||
req->xfer[0].tx_buf = &req->reset;
|
||||
req->xfer[0].len = 2;
|
||||
req->xfer[0].cs_change = 1;
|
||||
|
||||
req->xfer[1].tx_buf = &req->ref_on;
|
||||
req->xfer[1].len = 2;
|
||||
req->xfer[1].delay_usecs = ts->vref_delay_usecs;
|
||||
req->xfer[1].cs_change = 1;
|
||||
|
||||
req->xfer[2].tx_buf = &req->command;
|
||||
req->xfer[2].len = 2;
|
||||
req->xfer[2].delay_usecs = ts->vref_delay_usecs;
|
||||
req->xfer[2].cs_change = 1;
|
||||
|
||||
req->xfer[3].rx_buf = &req->sample;
|
||||
req->xfer[3].len = 2;
|
||||
req->xfer[3].cs_change = 1;
|
||||
|
||||
req->xfer[4].tx_buf = &ts->cmd_crtl2; /*REF OFF*/
|
||||
req->xfer[4].len = 2;
|
||||
req->xfer[4].cs_change = 1;
|
||||
|
||||
req->xfer[5].tx_buf = &ts->cmd_crtl1; /*DEFAULT*/
|
||||
req->xfer[5].len = 2;
|
||||
@ -327,7 +332,7 @@ static int ad7877_read_adc(struct spi_device *spi, unsigned command)
|
||||
return status ? : sample;
|
||||
}
|
||||
|
||||
static void ad7877_rx(struct ad7877 *ts)
|
||||
static int ad7877_process_data(struct ad7877 *ts)
|
||||
{
|
||||
struct input_dev *input_dev = ts->input;
|
||||
unsigned Rt;
|
||||
@ -354,11 +359,25 @@ static void ad7877_rx(struct ad7877 *ts)
|
||||
Rt /= z1;
|
||||
Rt = (Rt + 2047) >> 12;
|
||||
|
||||
/*
|
||||
* Sample found inconsistent, pressure is beyond
|
||||
* the maximum. Don't report it to user space.
|
||||
*/
|
||||
if (Rt > ts->pressure_max)
|
||||
return -EINVAL;
|
||||
|
||||
if (!timer_pending(&ts->timer))
|
||||
input_report_key(input_dev, BTN_TOUCH, 1);
|
||||
|
||||
input_report_abs(input_dev, ABS_X, x);
|
||||
input_report_abs(input_dev, ABS_Y, y);
|
||||
input_report_abs(input_dev, ABS_PRESSURE, Rt);
|
||||
input_sync(input_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline void ad7877_ts_event_release(struct ad7877 *ts)
|
||||
@ -366,72 +385,56 @@ static inline void ad7877_ts_event_release(struct ad7877 *ts)
|
||||
struct input_dev *input_dev = ts->input;
|
||||
|
||||
input_report_abs(input_dev, ABS_PRESSURE, 0);
|
||||
input_report_key(input_dev, BTN_TOUCH, 0);
|
||||
input_sync(input_dev);
|
||||
}
|
||||
|
||||
static void ad7877_timer(unsigned long handle)
|
||||
{
|
||||
struct ad7877 *ts = (void *)handle;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ts->lock, flags);
|
||||
ad7877_ts_event_release(ts);
|
||||
spin_unlock_irqrestore(&ts->lock, flags);
|
||||
}
|
||||
|
||||
static irqreturn_t ad7877_irq(int irq, void *handle)
|
||||
{
|
||||
struct ad7877 *ts = handle;
|
||||
unsigned long flags;
|
||||
int status;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* The repeated conversion sequencer controlled by TMR kicked off
|
||||
* too fast. We ignore the last and process the sample sequence
|
||||
* currently in the queue. It can't be older than 9.4ms, and we
|
||||
* need to avoid that ts->msg doesn't get issued twice while in work.
|
||||
*/
|
||||
error = spi_sync(ts->spi, &ts->msg);
|
||||
if (error) {
|
||||
dev_err(&ts->spi->dev, "spi_sync --> %d\n", error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ts->lock, flags);
|
||||
if (!ts->pending) {
|
||||
ts->pending = 1;
|
||||
|
||||
status = spi_async(ts->spi, &ts->msg);
|
||||
if (status)
|
||||
dev_err(&ts->spi->dev, "spi_sync --> %d\n", status);
|
||||
}
|
||||
error = ad7877_process_data(ts);
|
||||
if (!error)
|
||||
mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
|
||||
spin_unlock_irqrestore(&ts->lock, flags);
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ad7877_callback(void *_ts)
|
||||
{
|
||||
struct ad7877 *ts = _ts;
|
||||
|
||||
spin_lock_irq(&ts->lock);
|
||||
|
||||
ad7877_rx(ts);
|
||||
ts->pending = 0;
|
||||
mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
|
||||
|
||||
spin_unlock_irq(&ts->lock);
|
||||
}
|
||||
|
||||
static void ad7877_disable(struct ad7877 *ts)
|
||||
{
|
||||
mutex_lock(&ts->mutex);
|
||||
|
||||
if (!ts->disabled) {
|
||||
ts->disabled = 1;
|
||||
ts->disabled = true;
|
||||
disable_irq(ts->spi->irq);
|
||||
|
||||
/* Wait for spi_async callback */
|
||||
while (ts->pending)
|
||||
msleep(1);
|
||||
|
||||
if (del_timer_sync(&ts->timer))
|
||||
ad7877_ts_event_release(ts);
|
||||
}
|
||||
|
||||
/* we know the chip's in lowpower mode since we always
|
||||
/*
|
||||
* We know the chip's in lowpower mode since we always
|
||||
* leave it that way after every request
|
||||
*/
|
||||
|
||||
@ -443,7 +446,7 @@ static void ad7877_enable(struct ad7877 *ts)
|
||||
mutex_lock(&ts->mutex);
|
||||
|
||||
if (ts->disabled) {
|
||||
ts->disabled = 0;
|
||||
ts->disabled = false;
|
||||
enable_irq(ts->spi->irq);
|
||||
}
|
||||
|
||||
@ -453,7 +456,7 @@ static void ad7877_enable(struct ad7877 *ts)
|
||||
#define SHOW(name) static ssize_t \
|
||||
name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct ad7877 *ts = dev_get_drvdata(dev); \
|
||||
struct ad7877 *ts = dev_get_drvdata(dev); \
|
||||
ssize_t v = ad7877_read_adc(ts->spi, \
|
||||
AD7877_READ_CHAN(name)); \
|
||||
if (v < 0) \
|
||||
@ -473,7 +476,7 @@ SHOW(temp2)
|
||||
static ssize_t ad7877_disable_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", ts->disabled);
|
||||
}
|
||||
@ -503,7 +506,7 @@ static DEVICE_ATTR(disable, 0664, ad7877_disable_show, ad7877_disable_store);
|
||||
static ssize_t ad7877_dac_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", ts->dac);
|
||||
}
|
||||
@ -533,7 +536,7 @@ static DEVICE_ATTR(dac, 0664, ad7877_dac_show, ad7877_dac_store);
|
||||
static ssize_t ad7877_gpio3_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", ts->gpio3);
|
||||
}
|
||||
@ -564,7 +567,7 @@ static DEVICE_ATTR(gpio3, 0664, ad7877_gpio3_show, ad7877_gpio3_store);
|
||||
static ssize_t ad7877_gpio4_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", ts->gpio4);
|
||||
}
|
||||
@ -597,16 +600,35 @@ static struct attribute *ad7877_attributes[] = {
|
||||
&dev_attr_temp2.attr,
|
||||
&dev_attr_aux1.attr,
|
||||
&dev_attr_aux2.attr,
|
||||
&dev_attr_aux3.attr,
|
||||
&dev_attr_bat1.attr,
|
||||
&dev_attr_bat2.attr,
|
||||
&dev_attr_disable.attr,
|
||||
&dev_attr_dac.attr,
|
||||
&dev_attr_gpio3.attr,
|
||||
&dev_attr_gpio4.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static mode_t ad7877_attr_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int n)
|
||||
{
|
||||
mode_t mode = attr->mode;
|
||||
|
||||
if (attr == &dev_attr_aux3.attr) {
|
||||
if (gpio3)
|
||||
mode = 0;
|
||||
} else if (attr == &dev_attr_gpio3.attr) {
|
||||
if (!gpio3)
|
||||
mode = 0;
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
static const struct attribute_group ad7877_attr_group = {
|
||||
.attrs = ad7877_attributes,
|
||||
.is_visible = ad7877_attr_is_visible,
|
||||
.attrs = ad7877_attributes,
|
||||
};
|
||||
|
||||
static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts)
|
||||
@ -635,22 +657,25 @@ static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts)
|
||||
|
||||
spi_message_init(m);
|
||||
|
||||
m->complete = ad7877_callback;
|
||||
m->context = ts;
|
||||
|
||||
ts->xfer[0].tx_buf = &ts->cmd_crtl1;
|
||||
ts->xfer[0].len = 2;
|
||||
ts->xfer[0].cs_change = 1;
|
||||
|
||||
spi_message_add_tail(&ts->xfer[0], m);
|
||||
|
||||
ts->xfer[1].tx_buf = &ts->cmd_dummy; /* Send ZERO */
|
||||
ts->xfer[1].len = 2;
|
||||
ts->xfer[1].cs_change = 1;
|
||||
|
||||
spi_message_add_tail(&ts->xfer[1], m);
|
||||
|
||||
for (i = 0; i < 11; i++) {
|
||||
for (i = 0; i < AD7877_NR_SENSE; i++) {
|
||||
ts->xfer[i + 2].rx_buf = &ts->conversion_data[AD7877_SEQ_YPOS + i];
|
||||
ts->xfer[i + 2].len = 2;
|
||||
if (i < (AD7877_NR_SENSE - 1))
|
||||
ts->xfer[i + 2].cs_change = 1;
|
||||
spi_message_add_tail(&ts->xfer[i + 2], m);
|
||||
}
|
||||
}
|
||||
@ -718,6 +743,8 @@ static int __devinit ad7877_probe(struct spi_device *spi)
|
||||
input_dev->phys = ts->phys;
|
||||
input_dev->dev.parent = &spi->dev;
|
||||
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
__set_bit(ABS_X, input_dev->absbit);
|
||||
__set_bit(ABS_Y, input_dev->absbit);
|
||||
@ -752,8 +779,9 @@ static int __devinit ad7877_probe(struct spi_device *spi)
|
||||
|
||||
/* Request AD7877 /DAV GPIO interrupt */
|
||||
|
||||
err = request_irq(spi->irq, ad7877_irq, IRQF_TRIGGER_FALLING,
|
||||
spi->dev.driver->name, ts);
|
||||
err = request_threaded_irq(spi->irq, NULL, ad7877_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
spi->dev.driver->name, ts);
|
||||
if (err) {
|
||||
dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
|
||||
goto err_free_mem;
|
||||
@ -763,20 +791,12 @@ static int __devinit ad7877_probe(struct spi_device *spi)
|
||||
if (err)
|
||||
goto err_free_irq;
|
||||
|
||||
err = device_create_file(&spi->dev,
|
||||
gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3);
|
||||
err = input_register_device(input_dev);
|
||||
if (err)
|
||||
goto err_remove_attr_group;
|
||||
|
||||
err = input_register_device(input_dev);
|
||||
if (err)
|
||||
goto err_remove_attr;
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_attr:
|
||||
device_remove_file(&spi->dev,
|
||||
gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3);
|
||||
err_remove_attr_group:
|
||||
sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group);
|
||||
err_free_irq:
|
||||
@ -790,11 +810,9 @@ err_free_mem:
|
||||
|
||||
static int __devexit ad7877_remove(struct spi_device *spi)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(&spi->dev);
|
||||
struct ad7877 *ts = dev_get_drvdata(&spi->dev);
|
||||
|
||||
sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group);
|
||||
device_remove_file(&spi->dev,
|
||||
gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3);
|
||||
|
||||
ad7877_disable(ts);
|
||||
free_irq(ts->spi->irq, ts);
|
||||
|
File diff suppressed because it is too large
Load Diff
648
drivers/input/touchscreen/bu21013_ts.c
Normal file
648
drivers/input/touchscreen/bu21013_ts.c
Normal file
@ -0,0 +1,648 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
* Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
|
||||
* License terms:GNU General Public License (GPL) version 2
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/bu21013.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define PEN_DOWN_INTR 0
|
||||
#define MAX_FINGERS 2
|
||||
#define RESET_DELAY 30
|
||||
#define PENUP_TIMEOUT (10)
|
||||
#define DELTA_MIN 16
|
||||
#define MASK_BITS 0x03
|
||||
#define SHIFT_8 8
|
||||
#define SHIFT_2 2
|
||||
#define LENGTH_OF_BUFFER 11
|
||||
#define I2C_RETRY_COUNT 5
|
||||
|
||||
#define BU21013_SENSORS_BTN_0_7_REG 0x70
|
||||
#define BU21013_SENSORS_BTN_8_15_REG 0x71
|
||||
#define BU21013_SENSORS_BTN_16_23_REG 0x72
|
||||
#define BU21013_X1_POS_MSB_REG 0x73
|
||||
#define BU21013_X1_POS_LSB_REG 0x74
|
||||
#define BU21013_Y1_POS_MSB_REG 0x75
|
||||
#define BU21013_Y1_POS_LSB_REG 0x76
|
||||
#define BU21013_X2_POS_MSB_REG 0x77
|
||||
#define BU21013_X2_POS_LSB_REG 0x78
|
||||
#define BU21013_Y2_POS_MSB_REG 0x79
|
||||
#define BU21013_Y2_POS_LSB_REG 0x7A
|
||||
#define BU21013_INT_CLR_REG 0xE8
|
||||
#define BU21013_INT_MODE_REG 0xE9
|
||||
#define BU21013_GAIN_REG 0xEA
|
||||
#define BU21013_OFFSET_MODE_REG 0xEB
|
||||
#define BU21013_XY_EDGE_REG 0xEC
|
||||
#define BU21013_RESET_REG 0xED
|
||||
#define BU21013_CALIB_REG 0xEE
|
||||
#define BU21013_DONE_REG 0xEF
|
||||
#define BU21013_SENSOR_0_7_REG 0xF0
|
||||
#define BU21013_SENSOR_8_15_REG 0xF1
|
||||
#define BU21013_SENSOR_16_23_REG 0xF2
|
||||
#define BU21013_POS_MODE1_REG 0xF3
|
||||
#define BU21013_POS_MODE2_REG 0xF4
|
||||
#define BU21013_CLK_MODE_REG 0xF5
|
||||
#define BU21013_IDLE_REG 0xFA
|
||||
#define BU21013_FILTER_REG 0xFB
|
||||
#define BU21013_TH_ON_REG 0xFC
|
||||
#define BU21013_TH_OFF_REG 0xFD
|
||||
|
||||
|
||||
#define BU21013_RESET_ENABLE 0x01
|
||||
|
||||
#define BU21013_SENSORS_EN_0_7 0x3F
|
||||
#define BU21013_SENSORS_EN_8_15 0xFC
|
||||
#define BU21013_SENSORS_EN_16_23 0x1F
|
||||
|
||||
#define BU21013_POS_MODE1_0 0x02
|
||||
#define BU21013_POS_MODE1_1 0x04
|
||||
#define BU21013_POS_MODE1_2 0x08
|
||||
|
||||
#define BU21013_POS_MODE2_ZERO 0x01
|
||||
#define BU21013_POS_MODE2_AVG1 0x02
|
||||
#define BU21013_POS_MODE2_AVG2 0x04
|
||||
#define BU21013_POS_MODE2_EN_XY 0x08
|
||||
#define BU21013_POS_MODE2_EN_RAW 0x10
|
||||
#define BU21013_POS_MODE2_MULTI 0x80
|
||||
|
||||
#define BU21013_CLK_MODE_DIV 0x01
|
||||
#define BU21013_CLK_MODE_EXT 0x02
|
||||
#define BU21013_CLK_MODE_CALIB 0x80
|
||||
|
||||
#define BU21013_IDLET_0 0x01
|
||||
#define BU21013_IDLET_1 0x02
|
||||
#define BU21013_IDLET_2 0x04
|
||||
#define BU21013_IDLET_3 0x08
|
||||
#define BU21013_IDLE_INTERMIT_EN 0x10
|
||||
|
||||
#define BU21013_DELTA_0_6 0x7F
|
||||
#define BU21013_FILTER_EN 0x80
|
||||
|
||||
#define BU21013_INT_MODE_LEVEL 0x00
|
||||
#define BU21013_INT_MODE_EDGE 0x01
|
||||
|
||||
#define BU21013_GAIN_0 0x01
|
||||
#define BU21013_GAIN_1 0x02
|
||||
#define BU21013_GAIN_2 0x04
|
||||
|
||||
#define BU21013_OFFSET_MODE_DEFAULT 0x00
|
||||
#define BU21013_OFFSET_MODE_MOVE 0x01
|
||||
#define BU21013_OFFSET_MODE_DISABLE 0x02
|
||||
|
||||
#define BU21013_TH_ON_0 0x01
|
||||
#define BU21013_TH_ON_1 0x02
|
||||
#define BU21013_TH_ON_2 0x04
|
||||
#define BU21013_TH_ON_3 0x08
|
||||
#define BU21013_TH_ON_4 0x10
|
||||
#define BU21013_TH_ON_5 0x20
|
||||
#define BU21013_TH_ON_6 0x40
|
||||
#define BU21013_TH_ON_7 0x80
|
||||
#define BU21013_TH_ON_MAX 0xFF
|
||||
|
||||
#define BU21013_TH_OFF_0 0x01
|
||||
#define BU21013_TH_OFF_1 0x02
|
||||
#define BU21013_TH_OFF_2 0x04
|
||||
#define BU21013_TH_OFF_3 0x08
|
||||
#define BU21013_TH_OFF_4 0x10
|
||||
#define BU21013_TH_OFF_5 0x20
|
||||
#define BU21013_TH_OFF_6 0x40
|
||||
#define BU21013_TH_OFF_7 0x80
|
||||
#define BU21013_TH_OFF_MAX 0xFF
|
||||
|
||||
#define BU21013_X_EDGE_0 0x01
|
||||
#define BU21013_X_EDGE_1 0x02
|
||||
#define BU21013_X_EDGE_2 0x04
|
||||
#define BU21013_X_EDGE_3 0x08
|
||||
#define BU21013_Y_EDGE_0 0x10
|
||||
#define BU21013_Y_EDGE_1 0x20
|
||||
#define BU21013_Y_EDGE_2 0x40
|
||||
#define BU21013_Y_EDGE_3 0x80
|
||||
|
||||
#define BU21013_DONE 0x01
|
||||
#define BU21013_NUMBER_OF_X_SENSORS (6)
|
||||
#define BU21013_NUMBER_OF_Y_SENSORS (11)
|
||||
|
||||
#define DRIVER_TP "bu21013_tp"
|
||||
|
||||
/**
|
||||
* struct bu21013_ts_data - touch panel data structure
|
||||
* @client: pointer to the i2c client
|
||||
* @wait: variable to wait_queue_head_t structure
|
||||
* @touch_stopped: touch stop flag
|
||||
* @chip: pointer to the touch panel controller
|
||||
* @in_dev: pointer to the input device structure
|
||||
* @intr_pin: interrupt pin value
|
||||
*
|
||||
* Touch panel device data structure
|
||||
*/
|
||||
struct bu21013_ts_data {
|
||||
struct i2c_client *client;
|
||||
wait_queue_head_t wait;
|
||||
bool touch_stopped;
|
||||
const struct bu21013_platform_device *chip;
|
||||
struct input_dev *in_dev;
|
||||
unsigned int intr_pin;
|
||||
};
|
||||
|
||||
/**
|
||||
* bu21013_read_block_data(): read the touch co-ordinates
|
||||
* @data: bu21013_ts_data structure pointer
|
||||
* @buf: byte pointer
|
||||
*
|
||||
* Read the touch co-ordinates using i2c read block into buffer
|
||||
* and returns integer.
|
||||
*/
|
||||
static int bu21013_read_block_data(struct bu21013_ts_data *data, u8 *buf)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < I2C_RETRY_COUNT; i++) {
|
||||
ret = i2c_smbus_read_i2c_block_data
|
||||
(data->client, BU21013_SENSORS_BTN_0_7_REG,
|
||||
LENGTH_OF_BUFFER, buf);
|
||||
if (ret == LENGTH_OF_BUFFER)
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* bu21013_do_touch_report(): Get the touch co-ordinates
|
||||
* @data: bu21013_ts_data structure pointer
|
||||
*
|
||||
* Get the touch co-ordinates from touch sensor registers and writes
|
||||
* into device structure and returns integer.
|
||||
*/
|
||||
static int bu21013_do_touch_report(struct bu21013_ts_data *data)
|
||||
{
|
||||
u8 buf[LENGTH_OF_BUFFER];
|
||||
unsigned int pos_x[2], pos_y[2];
|
||||
bool has_x_sensors, has_y_sensors;
|
||||
int finger_down_count = 0;
|
||||
int i;
|
||||
|
||||
if (data == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (bu21013_read_block_data(data, buf) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
has_x_sensors = hweight32(buf[0] & BU21013_SENSORS_EN_0_7);
|
||||
has_y_sensors = hweight32(((buf[1] & BU21013_SENSORS_EN_8_15) |
|
||||
((buf[2] & BU21013_SENSORS_EN_16_23) << SHIFT_8)) >> SHIFT_2);
|
||||
if (!has_x_sensors || !has_y_sensors)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < MAX_FINGERS; i++) {
|
||||
const u8 *p = &buf[4 * i + 3];
|
||||
unsigned int x = p[0] << SHIFT_2 | (p[1] & MASK_BITS);
|
||||
unsigned int y = p[2] << SHIFT_2 | (p[3] & MASK_BITS);
|
||||
if (x == 0 || y == 0)
|
||||
continue;
|
||||
pos_x[finger_down_count] = x;
|
||||
pos_y[finger_down_count] = y;
|
||||
finger_down_count++;
|
||||
}
|
||||
|
||||
if (finger_down_count) {
|
||||
if (finger_down_count == 2 &&
|
||||
(abs(pos_x[0] - pos_x[1]) < DELTA_MIN ||
|
||||
abs(pos_y[0] - pos_y[1]) < DELTA_MIN)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < finger_down_count; i++) {
|
||||
if (data->chip->x_flip)
|
||||
pos_x[i] = data->chip->touch_x_max - pos_x[i];
|
||||
if (data->chip->y_flip)
|
||||
pos_y[i] = data->chip->touch_y_max - pos_y[i];
|
||||
|
||||
input_report_abs(data->in_dev,
|
||||
ABS_MT_POSITION_X, pos_x[i]);
|
||||
input_report_abs(data->in_dev,
|
||||
ABS_MT_POSITION_Y, pos_y[i]);
|
||||
input_mt_sync(data->in_dev);
|
||||
}
|
||||
} else
|
||||
input_mt_sync(data->in_dev);
|
||||
|
||||
input_sync(data->in_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* bu21013_gpio_irq() - gpio thread function for touch interrupt
|
||||
* @irq: irq value
|
||||
* @device_data: void pointer
|
||||
*
|
||||
* This gpio thread function for touch interrupt
|
||||
* and returns irqreturn_t.
|
||||
*/
|
||||
static irqreturn_t bu21013_gpio_irq(int irq, void *device_data)
|
||||
{
|
||||
struct bu21013_ts_data *data = device_data;
|
||||
struct i2c_client *i2c = data->client;
|
||||
int retval;
|
||||
|
||||
do {
|
||||
retval = bu21013_do_touch_report(data);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "bu21013_do_touch_report failed\n");
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
data->intr_pin = data->chip->irq_read_val();
|
||||
if (data->intr_pin == PEN_DOWN_INTR)
|
||||
wait_event_timeout(data->wait, data->touch_stopped,
|
||||
msecs_to_jiffies(2));
|
||||
} while (!data->intr_pin && !data->touch_stopped);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* bu21013_init_chip() - power on sequence for the bu21013 controller
|
||||
* @data: device structure pointer
|
||||
*
|
||||
* This function is used to power on
|
||||
* the bu21013 controller and returns integer.
|
||||
*/
|
||||
static int bu21013_init_chip(struct bu21013_ts_data *data)
|
||||
{
|
||||
int retval;
|
||||
struct i2c_client *i2c = data->client;
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_RESET_REG,
|
||||
BU21013_RESET_ENABLE);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_RESET reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
msleep(RESET_DELAY);
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_0_7_REG,
|
||||
BU21013_SENSORS_EN_0_7);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_SENSOR_0_7 reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_8_15_REG,
|
||||
BU21013_SENSORS_EN_8_15);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_SENSOR_8_15 reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_16_23_REG,
|
||||
BU21013_SENSORS_EN_16_23);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_SENSOR_16_23 reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE1_REG,
|
||||
(BU21013_POS_MODE1_0 | BU21013_POS_MODE1_1));
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_POS_MODE1 reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE2_REG,
|
||||
(BU21013_POS_MODE2_ZERO | BU21013_POS_MODE2_AVG1 |
|
||||
BU21013_POS_MODE2_AVG2 | BU21013_POS_MODE2_EN_RAW |
|
||||
BU21013_POS_MODE2_MULTI));
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_POS_MODE2 reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (data->chip->ext_clk)
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG,
|
||||
(BU21013_CLK_MODE_EXT | BU21013_CLK_MODE_CALIB));
|
||||
else
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG,
|
||||
(BU21013_CLK_MODE_DIV | BU21013_CLK_MODE_CALIB));
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_CLK_MODE reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_IDLE_REG,
|
||||
(BU21013_IDLET_0 | BU21013_IDLE_INTERMIT_EN));
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_IDLE reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_INT_MODE_REG,
|
||||
BU21013_INT_MODE_LEVEL);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_INT_MODE reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_FILTER_REG,
|
||||
(BU21013_DELTA_0_6 |
|
||||
BU21013_FILTER_EN));
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_FILTER reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_ON_REG,
|
||||
BU21013_TH_ON_5);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_TH_ON reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_OFF_REG,
|
||||
BU21013_TH_OFF_4 || BU21013_TH_OFF_3);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_TH_OFF reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_GAIN_REG,
|
||||
(BU21013_GAIN_0 | BU21013_GAIN_1));
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_GAIN reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_OFFSET_MODE_REG,
|
||||
BU21013_OFFSET_MODE_DEFAULT);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_OFFSET_MODE reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_XY_EDGE_REG,
|
||||
(BU21013_X_EDGE_0 | BU21013_X_EDGE_2 |
|
||||
BU21013_Y_EDGE_1 | BU21013_Y_EDGE_3));
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_XY_EDGE reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_DONE_REG,
|
||||
BU21013_DONE);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_REG_DONE reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* bu21013_free_irq() - frees IRQ registered for touchscreen
|
||||
* @bu21013_data: device structure pointer
|
||||
*
|
||||
* This function signals interrupt thread to stop processing and
|
||||
* frees interrupt.
|
||||
*/
|
||||
static void bu21013_free_irq(struct bu21013_ts_data *bu21013_data)
|
||||
{
|
||||
bu21013_data->touch_stopped = true;
|
||||
wake_up(&bu21013_data->wait);
|
||||
free_irq(bu21013_data->chip->irq, bu21013_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* bu21013_probe() - initializes the i2c-client touchscreen driver
|
||||
* @client: i2c client structure pointer
|
||||
* @id: i2c device id pointer
|
||||
*
|
||||
* This function used to initializes the i2c-client touchscreen
|
||||
* driver and returns integer.
|
||||
*/
|
||||
static int __devinit bu21013_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct bu21013_ts_data *bu21013_data;
|
||||
struct input_dev *in_dev;
|
||||
const struct bu21013_platform_device *pdata =
|
||||
client->dev.platform_data;
|
||||
int error;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||
dev_err(&client->dev, "i2c smbus byte data not supported\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&client->dev, "platform data not defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bu21013_data = kzalloc(sizeof(struct bu21013_ts_data), GFP_KERNEL);
|
||||
in_dev = input_allocate_device();
|
||||
if (!bu21013_data || !in_dev) {
|
||||
dev_err(&client->dev, "device memory alloc failed\n");
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
bu21013_data->in_dev = in_dev;
|
||||
bu21013_data->chip = pdata;
|
||||
bu21013_data->client = client;
|
||||
bu21013_data->touch_stopped = false;
|
||||
init_waitqueue_head(&bu21013_data->wait);
|
||||
|
||||
/* configure the gpio pins */
|
||||
if (pdata->cs_en) {
|
||||
error = pdata->cs_en(pdata->cs_pin);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev, "chip init failed\n");
|
||||
goto err_free_mem;
|
||||
}
|
||||
}
|
||||
|
||||
/* configure the touch panel controller */
|
||||
error = bu21013_init_chip(bu21013_data);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "error in bu21013 config\n");
|
||||
goto err_cs_disable;
|
||||
}
|
||||
|
||||
/* register the device to input subsystem */
|
||||
in_dev->name = DRIVER_TP;
|
||||
in_dev->id.bustype = BUS_I2C;
|
||||
in_dev->dev.parent = &client->dev;
|
||||
|
||||
__set_bit(EV_SYN, in_dev->evbit);
|
||||
__set_bit(EV_KEY, in_dev->evbit);
|
||||
__set_bit(EV_ABS, in_dev->evbit);
|
||||
|
||||
input_set_abs_params(in_dev, ABS_MT_POSITION_X, 0,
|
||||
pdata->x_max_res, 0, 0);
|
||||
input_set_abs_params(in_dev, ABS_MT_POSITION_Y, 0,
|
||||
pdata->y_max_res, 0, 0);
|
||||
input_set_drvdata(in_dev, bu21013_data);
|
||||
|
||||
error = request_threaded_irq(pdata->irq, NULL, bu21013_gpio_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_SHARED,
|
||||
DRIVER_TP, bu21013_data);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "request irq %d failed\n", pdata->irq);
|
||||
goto err_cs_disable;
|
||||
}
|
||||
|
||||
error = input_register_device(in_dev);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "failed to register input device\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
device_init_wakeup(&client->dev, pdata->wakeup);
|
||||
i2c_set_clientdata(client, bu21013_data);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
bu21013_free_irq(bu21013_data);
|
||||
err_cs_disable:
|
||||
pdata->cs_dis(pdata->cs_pin);
|
||||
err_free_mem:
|
||||
input_free_device(bu21013_data->in_dev);
|
||||
kfree(bu21013_data);
|
||||
|
||||
return error;
|
||||
}
|
||||
/**
|
||||
* bu21013_remove() - removes the i2c-client touchscreen driver
|
||||
* @client: i2c client structure pointer
|
||||
*
|
||||
* This function uses to remove the i2c-client
|
||||
* touchscreen driver and returns integer.
|
||||
*/
|
||||
static int __devexit bu21013_remove(struct i2c_client *client)
|
||||
{
|
||||
struct bu21013_ts_data *bu21013_data = i2c_get_clientdata(client);
|
||||
|
||||
bu21013_free_irq(bu21013_data);
|
||||
|
||||
bu21013_data->chip->cs_dis(bu21013_data->chip->cs_pin);
|
||||
|
||||
input_unregister_device(bu21013_data->in_dev);
|
||||
kfree(bu21013_data);
|
||||
|
||||
device_init_wakeup(&client->dev, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/**
|
||||
* bu21013_suspend() - suspend the touch screen controller
|
||||
* @dev: pointer to device structure
|
||||
*
|
||||
* This function is used to suspend the
|
||||
* touch panel controller and returns integer
|
||||
*/
|
||||
static int bu21013_suspend(struct device *dev)
|
||||
{
|
||||
struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = bu21013_data->client;
|
||||
|
||||
bu21013_data->touch_stopped = true;
|
||||
if (device_may_wakeup(&client->dev))
|
||||
enable_irq_wake(bu21013_data->chip->irq);
|
||||
else
|
||||
disable_irq(bu21013_data->chip->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* bu21013_resume() - resume the touch screen controller
|
||||
* @dev: pointer to device structure
|
||||
*
|
||||
* This function is used to resume the touch panel
|
||||
* controller and returns integer.
|
||||
*/
|
||||
static int bu21013_resume(struct device *dev)
|
||||
{
|
||||
struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = bu21013_data->client;
|
||||
int retval;
|
||||
|
||||
retval = bu21013_init_chip(bu21013_data);
|
||||
if (retval < 0) {
|
||||
dev_err(&client->dev, "bu21013 controller config failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
bu21013_data->touch_stopped = false;
|
||||
|
||||
if (device_may_wakeup(&client->dev))
|
||||
disable_irq_wake(bu21013_data->chip->irq);
|
||||
else
|
||||
enable_irq(bu21013_data->chip->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops bu21013_dev_pm_ops = {
|
||||
.suspend = bu21013_suspend,
|
||||
.resume = bu21013_resume,
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id bu21013_id[] = {
|
||||
{ DRIVER_TP, 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bu21013_id);
|
||||
|
||||
static struct i2c_driver bu21013_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_TP,
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &bu21013_dev_pm_ops,
|
||||
#endif
|
||||
},
|
||||
.probe = bu21013_probe,
|
||||
.remove = __devexit_p(bu21013_remove),
|
||||
.id_table = bu21013_id,
|
||||
};
|
||||
|
||||
/**
|
||||
* bu21013_init() - initializes the bu21013 touchscreen driver
|
||||
*
|
||||
* This function used to initializes the bu21013
|
||||
* touchscreen driver and returns integer.
|
||||
*/
|
||||
static int __init bu21013_init(void)
|
||||
{
|
||||
return i2c_add_driver(&bu21013_driver);
|
||||
}
|
||||
|
||||
/**
|
||||
* bu21013_exit() - de-initializes the bu21013 touchscreen driver
|
||||
*
|
||||
* This function uses to de-initializes the bu21013
|
||||
* touchscreen driver and returns none.
|
||||
*/
|
||||
static void __exit bu21013_exit(void)
|
||||
{
|
||||
i2c_del_driver(&bu21013_driver);
|
||||
}
|
||||
|
||||
module_init(bu21013_init);
|
||||
module_exit(bu21013_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Naveen Kumar G <naveen.gaddipati@stericsson.com>");
|
||||
MODULE_DESCRIPTION("bu21013 touch screen controller driver");
|
@ -206,9 +206,9 @@ static int __devinit cy8ctmg110_probe(struct i2c_client *client,
|
||||
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X,
|
||||
CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 0, 0);
|
||||
CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 4, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y,
|
||||
CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 0, 0);
|
||||
CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 4, 0);
|
||||
|
||||
if (ts->reset_pin) {
|
||||
err = gpio_request(ts->reset_pin, NULL);
|
||||
|
@ -107,8 +107,7 @@ static int __init hp680_ts_init(void)
|
||||
return 0;
|
||||
|
||||
fail2: free_irq(HP680_TS_IRQ, NULL);
|
||||
cancel_delayed_work(&work);
|
||||
flush_scheduled_work();
|
||||
cancel_delayed_work_sync(&work);
|
||||
fail1: input_free_device(hp680_ts_dev);
|
||||
return err;
|
||||
}
|
||||
@ -116,8 +115,7 @@ static int __init hp680_ts_init(void)
|
||||
static void __exit hp680_ts_exit(void)
|
||||
{
|
||||
free_irq(HP680_TS_IRQ, NULL);
|
||||
cancel_delayed_work(&work);
|
||||
flush_scheduled_work();
|
||||
cancel_delayed_work_sync(&work);
|
||||
input_unregister_device(hp680_ts_dev);
|
||||
}
|
||||
|
||||
|
687
drivers/input/touchscreen/intel-mid-touch.c
Normal file
687
drivers/input/touchscreen/intel-mid-touch.c
Normal file
@ -0,0 +1,687 @@
|
||||
/*
|
||||
* Intel MID Resistive Touch Screen Driver
|
||||
*
|
||||
* Copyright (C) 2008 Intel Corp
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*
|
||||
* Questions/Comments/Bug fixes to Sreedhara (sreedhara.ds@intel.com)
|
||||
* Ramesh Agarwal (ramesh.agarwal@intel.com)
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* TODO:
|
||||
* review conversion of r/m/w sequences
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/param.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
/* PMIC Interrupt registers */
|
||||
#define PMIC_REG_ID1 0x00 /* PMIC ID1 register */
|
||||
|
||||
/* PMIC Interrupt registers */
|
||||
#define PMIC_REG_INT 0x04 /* PMIC interrupt register */
|
||||
#define PMIC_REG_MINT 0x05 /* PMIC interrupt mask register */
|
||||
|
||||
/* ADC Interrupt registers */
|
||||
#define PMIC_REG_ADCINT 0x5F /* ADC interrupt register */
|
||||
#define PMIC_REG_MADCINT 0x60 /* ADC interrupt mask register */
|
||||
|
||||
/* ADC Control registers */
|
||||
#define PMIC_REG_ADCCNTL1 0x61 /* ADC control register */
|
||||
|
||||
/* ADC Channel Selection registers */
|
||||
#define PMICADDR0 0xA4
|
||||
#define END_OF_CHANNEL 0x1F
|
||||
|
||||
/* ADC Result register */
|
||||
#define PMIC_REG_ADCSNS0H 0x64
|
||||
|
||||
/* ADC channels for touch screen */
|
||||
#define MRST_TS_CHAN10 0xA /* Touch screen X+ connection */
|
||||
#define MRST_TS_CHAN11 0xB /* Touch screen X- connection */
|
||||
#define MRST_TS_CHAN12 0xC /* Touch screen Y+ connection */
|
||||
#define MRST_TS_CHAN13 0xD /* Touch screen Y- connection */
|
||||
|
||||
/* Touch screen channel BIAS constants */
|
||||
#define MRST_XBIAS 0x20
|
||||
#define MRST_YBIAS 0x40
|
||||
#define MRST_ZBIAS 0x80
|
||||
|
||||
/* Touch screen coordinates */
|
||||
#define MRST_X_MIN 10
|
||||
#define MRST_X_MAX 1024
|
||||
#define MRST_X_FUZZ 5
|
||||
#define MRST_Y_MIN 10
|
||||
#define MRST_Y_MAX 1024
|
||||
#define MRST_Y_FUZZ 5
|
||||
#define MRST_PRESSURE_MIN 0
|
||||
#define MRST_PRESSURE_NOMINAL 50
|
||||
#define MRST_PRESSURE_MAX 100
|
||||
|
||||
#define WAIT_ADC_COMPLETION 10 /* msec */
|
||||
|
||||
/* PMIC ADC round robin delays */
|
||||
#define ADC_LOOP_DELAY0 0x0 /* Continuous loop */
|
||||
#define ADC_LOOP_DELAY1 0x1 /* 4.5 ms approximate */
|
||||
|
||||
/* PMIC Vendor Identifiers */
|
||||
#define PMIC_VENDOR_FS 0 /* PMIC vendor FreeScale */
|
||||
#define PMIC_VENDOR_MAXIM 1 /* PMIC vendor MAXIM */
|
||||
#define PMIC_VENDOR_NEC 2 /* PMIC vendor NEC */
|
||||
#define MRSTOUCH_MAX_CHANNELS 32 /* Maximum ADC channels */
|
||||
|
||||
/* Touch screen device structure */
|
||||
struct mrstouch_dev {
|
||||
struct device *dev; /* device associated with touch screen */
|
||||
struct input_dev *input;
|
||||
char phys[32];
|
||||
u16 asr; /* Address selection register */
|
||||
int irq;
|
||||
unsigned int vendor; /* PMIC vendor */
|
||||
unsigned int rev; /* PMIC revision */
|
||||
|
||||
int (*read_prepare)(struct mrstouch_dev *tsdev);
|
||||
int (*read)(struct mrstouch_dev *tsdev, u16 *x, u16 *y, u16 *z);
|
||||
int (*read_finish)(struct mrstouch_dev *tsdev);
|
||||
};
|
||||
|
||||
|
||||
/*************************** NEC and Maxim Interface ************************/
|
||||
|
||||
static int mrstouch_nec_adc_read_prepare(struct mrstouch_dev *tsdev)
|
||||
{
|
||||
return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0, 0x20);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enables PENDET interrupt.
|
||||
*/
|
||||
static int mrstouch_nec_adc_read_finish(struct mrstouch_dev *tsdev)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x20, 0x20);
|
||||
if (!err)
|
||||
err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, 0, 0x05);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads PMIC ADC touch screen result
|
||||
* Reads ADC storage registers for higher 7 and lower 3 bits and
|
||||
* converts the two readings into a single value and turns off gain bit
|
||||
*/
|
||||
static int mrstouch_ts_chan_read(u16 offset, u16 chan, u16 *vp, u16 *vm)
|
||||
{
|
||||
int err;
|
||||
u16 result;
|
||||
u32 res;
|
||||
|
||||
result = PMIC_REG_ADCSNS0H + offset;
|
||||
|
||||
if (chan == MRST_TS_CHAN12)
|
||||
result += 4;
|
||||
|
||||
err = intel_scu_ipc_ioread32(result, &res);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Mash the bits up */
|
||||
|
||||
*vp = (res & 0xFF) << 3; /* Highest 7 bits */
|
||||
*vp |= (res >> 8) & 0x07; /* Lower 3 bits */
|
||||
*vp &= 0x3FF;
|
||||
|
||||
res >>= 16;
|
||||
|
||||
*vm = (res & 0xFF) << 3; /* Highest 7 bits */
|
||||
*vm |= (res >> 8) & 0x07; /* Lower 3 bits */
|
||||
*vm &= 0x3FF;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enables X, Y and Z bias values
|
||||
* Enables YPYM for X channels and XPXM for Y channels
|
||||
*/
|
||||
static int mrstouch_ts_bias_set(uint offset, uint bias)
|
||||
{
|
||||
int count;
|
||||
u16 chan, start;
|
||||
u16 reg[4];
|
||||
u8 data[4];
|
||||
|
||||
chan = PMICADDR0 + offset;
|
||||
start = MRST_TS_CHAN10;
|
||||
|
||||
for (count = 0; count <= 3; count++) {
|
||||
reg[count] = chan++;
|
||||
data[count] = bias | (start + count);
|
||||
}
|
||||
|
||||
return intel_scu_ipc_writev(reg, data, 4);
|
||||
}
|
||||
|
||||
/* To read touch screen channel values */
|
||||
static int mrstouch_nec_adc_read(struct mrstouch_dev *tsdev,
|
||||
u16 *x, u16 *y, u16 *z)
|
||||
{
|
||||
int err;
|
||||
u16 xm, ym, zm;
|
||||
|
||||
/* configure Y bias for X channels */
|
||||
err = mrstouch_ts_bias_set(tsdev->asr, MRST_YBIAS);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
msleep(WAIT_ADC_COMPLETION);
|
||||
|
||||
/* read x+ and x- channels */
|
||||
err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, x, &xm);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
/* configure x bias for y channels */
|
||||
err = mrstouch_ts_bias_set(tsdev->asr, MRST_XBIAS);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
msleep(WAIT_ADC_COMPLETION);
|
||||
|
||||
/* read y+ and y- channels */
|
||||
err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN12, y, &ym);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
/* configure z bias for x and y channels */
|
||||
err = mrstouch_ts_bias_set(tsdev->asr, MRST_ZBIAS);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
msleep(WAIT_ADC_COMPLETION);
|
||||
|
||||
/* read z+ and z- channels */
|
||||
err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, z, &zm);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
return 0;
|
||||
|
||||
ipc_error:
|
||||
dev_err(tsdev->dev, "ipc error during adc read\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/*************************** Freescale Interface ************************/
|
||||
|
||||
static int mrstouch_fs_adc_read_prepare(struct mrstouch_dev *tsdev)
|
||||
{
|
||||
int err, count;
|
||||
u16 chan;
|
||||
u16 reg[5];
|
||||
u8 data[5];
|
||||
|
||||
/* Stop the ADC */
|
||||
err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x00, 0x02);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
chan = PMICADDR0 + tsdev->asr;
|
||||
|
||||
/* Set X BIAS */
|
||||
for (count = 0; count <= 3; count++) {
|
||||
reg[count] = chan++;
|
||||
data[count] = 0x2A;
|
||||
}
|
||||
reg[count] = chan++; /* Dummy */
|
||||
data[count] = 0;
|
||||
|
||||
err = intel_scu_ipc_writev(reg, data, 5);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
msleep(WAIT_ADC_COMPLETION);
|
||||
|
||||
/* Set Y BIAS */
|
||||
for (count = 0; count <= 3; count++) {
|
||||
reg[count] = chan++;
|
||||
data[count] = 0x4A;
|
||||
}
|
||||
reg[count] = chan++; /* Dummy */
|
||||
data[count] = 0;
|
||||
|
||||
err = intel_scu_ipc_writev(reg, data, 5);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
msleep(WAIT_ADC_COMPLETION);
|
||||
|
||||
/* Set Z BIAS */
|
||||
err = intel_scu_ipc_iowrite32(chan + 2, 0x8A8A8A8A);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
msleep(WAIT_ADC_COMPLETION);
|
||||
|
||||
return 0;
|
||||
|
||||
ipc_error:
|
||||
dev_err(tsdev->dev, "ipc error during %s\n", __func__);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mrstouch_fs_adc_read(struct mrstouch_dev *tsdev,
|
||||
u16 *x, u16 *y, u16 *z)
|
||||
{
|
||||
int err;
|
||||
u16 result;
|
||||
u16 reg[4];
|
||||
u8 data[4];
|
||||
|
||||
result = PMIC_REG_ADCSNS0H + tsdev->asr;
|
||||
|
||||
reg[0] = result + 4;
|
||||
reg[1] = result + 5;
|
||||
reg[2] = result + 16;
|
||||
reg[3] = result + 17;
|
||||
|
||||
err = intel_scu_ipc_readv(reg, data, 4);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
*x = data[0] << 3; /* Higher 7 bits */
|
||||
*x |= data[1] & 0x7; /* Lower 3 bits */
|
||||
*x &= 0x3FF;
|
||||
|
||||
*y = data[2] << 3; /* Higher 7 bits */
|
||||
*y |= data[3] & 0x7; /* Lower 3 bits */
|
||||
*y &= 0x3FF;
|
||||
|
||||
/* Read Z value */
|
||||
reg[0] = result + 28;
|
||||
reg[1] = result + 29;
|
||||
|
||||
err = intel_scu_ipc_readv(reg, data, 4);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
*z = data[0] << 3; /* Higher 7 bits */
|
||||
*z |= data[1] & 0x7; /* Lower 3 bits */
|
||||
*z &= 0x3FF;
|
||||
|
||||
return 0;
|
||||
|
||||
ipc_error:
|
||||
dev_err(tsdev->dev, "ipc error during %s\n", __func__);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mrstouch_fs_adc_read_finish(struct mrstouch_dev *tsdev)
|
||||
{
|
||||
int err, count;
|
||||
u16 chan;
|
||||
u16 reg[5];
|
||||
u8 data[5];
|
||||
|
||||
/* Clear all TS channels */
|
||||
chan = PMICADDR0 + tsdev->asr;
|
||||
for (count = 0; count <= 4; count++) {
|
||||
reg[count] = chan++;
|
||||
data[count] = 0;
|
||||
}
|
||||
err = intel_scu_ipc_writev(reg, data, 5);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
for (count = 0; count <= 4; count++) {
|
||||
reg[count] = chan++;
|
||||
data[count] = 0;
|
||||
}
|
||||
err = intel_scu_ipc_writev(reg, data, 5);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
err = intel_scu_ipc_iowrite32(chan + 2, 0x00000000);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
/* Start ADC */
|
||||
err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x02, 0x02);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
return 0;
|
||||
|
||||
ipc_error:
|
||||
dev_err(tsdev->dev, "ipc error during %s\n", __func__);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void mrstouch_report_event(struct input_dev *input,
|
||||
unsigned int x, unsigned int y, unsigned int z)
|
||||
{
|
||||
if (z > MRST_PRESSURE_NOMINAL) {
|
||||
/* Pen touched, report button touch and coordinates */
|
||||
input_report_key(input, BTN_TOUCH, 1);
|
||||
input_report_abs(input, ABS_X, x);
|
||||
input_report_abs(input, ABS_Y, y);
|
||||
} else {
|
||||
input_report_key(input, BTN_TOUCH, 0);
|
||||
}
|
||||
|
||||
input_report_abs(input, ABS_PRESSURE, z);
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
/* PENDET interrupt handler */
|
||||
static irqreturn_t mrstouch_pendet_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct mrstouch_dev *tsdev = dev_id;
|
||||
u16 x, y, z;
|
||||
|
||||
/*
|
||||
* Should we lower thread priority? Probably not, since we are
|
||||
* not spinning but sleeping...
|
||||
*/
|
||||
|
||||
if (tsdev->read_prepare(tsdev))
|
||||
goto out;
|
||||
|
||||
do {
|
||||
if (tsdev->read(tsdev, &x, &y, &z))
|
||||
break;
|
||||
|
||||
mrstouch_report_event(tsdev->input, x, y, z);
|
||||
} while (z > MRST_PRESSURE_NOMINAL);
|
||||
|
||||
tsdev->read_finish(tsdev);
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Utility to read PMIC ID */
|
||||
static int __devinit mrstouch_read_pmic_id(uint *vendor, uint *rev)
|
||||
{
|
||||
int err;
|
||||
u8 r;
|
||||
|
||||
err = intel_scu_ipc_ioread8(PMIC_REG_ID1, &r);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*vendor = r & 0x7;
|
||||
*rev = (r >> 3) & 0x7;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse ADC channels to find end of the channel configured by other ADC user
|
||||
* NEC and MAXIM requires 4 channels and FreeScale needs 18 channels
|
||||
*/
|
||||
static int __devinit mrstouch_chan_parse(struct mrstouch_dev *tsdev)
|
||||
{
|
||||
int err, i, found;
|
||||
u8 r8;
|
||||
|
||||
found = -1;
|
||||
|
||||
for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) {
|
||||
if (found >= 0)
|
||||
break;
|
||||
|
||||
err = intel_scu_ipc_ioread8(PMICADDR0 + i, &r8);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (r8 == END_OF_CHANNEL) {
|
||||
found = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found < 0)
|
||||
return 0;
|
||||
|
||||
if (tsdev->vendor == PMIC_VENDOR_FS) {
|
||||
if (found && found > (MRSTOUCH_MAX_CHANNELS - 18))
|
||||
return -ENOSPC;
|
||||
} else {
|
||||
if (found && found > (MRSTOUCH_MAX_CHANNELS - 4))
|
||||
return -ENOSPC;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Writes touch screen channels to ADC address selection registers
|
||||
*/
|
||||
static int __devinit mrstouch_ts_chan_set(uint offset)
|
||||
{
|
||||
u16 chan;
|
||||
|
||||
int ret, count;
|
||||
|
||||
chan = PMICADDR0 + offset;
|
||||
for (count = 0; count <= 3; count++) {
|
||||
ret = intel_scu_ipc_iowrite8(chan++, MRST_TS_CHAN10 + count);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return intel_scu_ipc_iowrite8(chan++, END_OF_CHANNEL);
|
||||
}
|
||||
|
||||
/* Initialize ADC */
|
||||
static int __devinit mrstouch_adc_init(struct mrstouch_dev *tsdev)
|
||||
{
|
||||
int err, start;
|
||||
u8 ra, rm;
|
||||
|
||||
err = mrstouch_read_pmic_id(&tsdev->vendor, &tsdev->rev);
|
||||
if (err) {
|
||||
dev_err(tsdev->dev, "Unable to read PMIC id\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
switch (tsdev->vendor) {
|
||||
case PMIC_VENDOR_NEC:
|
||||
case PMIC_VENDOR_MAXIM:
|
||||
tsdev->read_prepare = mrstouch_nec_adc_read_prepare;
|
||||
tsdev->read = mrstouch_nec_adc_read;
|
||||
tsdev->read_finish = mrstouch_nec_adc_read_finish;
|
||||
break;
|
||||
|
||||
case PMIC_VENDOR_FS:
|
||||
tsdev->read_prepare = mrstouch_fs_adc_read_prepare;
|
||||
tsdev->read = mrstouch_fs_adc_read;
|
||||
tsdev->read_finish = mrstouch_fs_adc_read_finish;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(tsdev->dev,
|
||||
"Unsupported touchscreen: %d\n", tsdev->vendor);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
start = mrstouch_chan_parse(tsdev);
|
||||
if (start < 0) {
|
||||
dev_err(tsdev->dev, "Unable to parse channels\n");
|
||||
return start;
|
||||
}
|
||||
|
||||
tsdev->asr = start;
|
||||
|
||||
/*
|
||||
* ADC power on, start, enable PENDET and set loop delay
|
||||
* ADC loop delay is set to 4.5 ms approximately
|
||||
* Loop delay more than this results in jitter in adc readings
|
||||
* Setting loop delay to 0 (continous loop) in MAXIM stops PENDET
|
||||
* interrupt generation sometimes.
|
||||
*/
|
||||
|
||||
if (tsdev->vendor == PMIC_VENDOR_FS) {
|
||||
ra = 0xE0 | ADC_LOOP_DELAY0;
|
||||
rm = 0x5;
|
||||
} else {
|
||||
/* NEC and MAXIm not consistent with loop delay 0 */
|
||||
ra = 0xE0 | ADC_LOOP_DELAY1;
|
||||
rm = 0x0;
|
||||
|
||||
/* configure touch screen channels */
|
||||
err = mrstouch_ts_chan_set(tsdev->asr);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, ra, 0xE7);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, rm, 0x03);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Probe function for touch screen driver */
|
||||
static int __devinit mrstouch_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mrstouch_dev *tsdev;
|
||||
struct input_dev *input;
|
||||
int err;
|
||||
int irq;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no interrupt assigned\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tsdev = kzalloc(sizeof(struct mrstouch_dev), GFP_KERNEL);
|
||||
input = input_allocate_device();
|
||||
if (!tsdev || !input) {
|
||||
dev_err(&pdev->dev, "unable to allocate memory\n");
|
||||
err = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
tsdev->dev = &pdev->dev;
|
||||
tsdev->input = input;
|
||||
tsdev->irq = irq;
|
||||
|
||||
snprintf(tsdev->phys, sizeof(tsdev->phys),
|
||||
"%s/input0", dev_name(tsdev->dev));
|
||||
|
||||
err = mrstouch_adc_init(tsdev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "ADC initialization failed\n");
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
input->name = "mrst_touchscreen";
|
||||
input->phys = tsdev->phys;
|
||||
input->dev.parent = tsdev->dev;
|
||||
|
||||
input->id.vendor = tsdev->vendor;
|
||||
input->id.version = tsdev->rev;
|
||||
|
||||
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||
|
||||
input_set_abs_params(tsdev->input, ABS_X,
|
||||
MRST_X_MIN, MRST_X_MAX, MRST_X_FUZZ, 0);
|
||||
input_set_abs_params(tsdev->input, ABS_Y,
|
||||
MRST_Y_MIN, MRST_Y_MAX, MRST_Y_FUZZ, 0);
|
||||
input_set_abs_params(tsdev->input, ABS_PRESSURE,
|
||||
MRST_PRESSURE_MIN, MRST_PRESSURE_MAX, 0, 0);
|
||||
|
||||
err = request_threaded_irq(tsdev->irq, NULL, mrstouch_pendet_irq,
|
||||
0, "mrstouch", tsdev);
|
||||
if (err) {
|
||||
dev_err(tsdev->dev, "unable to allocate irq\n");
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
err = input_register_device(tsdev->input);
|
||||
if (err) {
|
||||
dev_err(tsdev->dev, "unable to register input device\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, tsdev);
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(tsdev->irq, tsdev);
|
||||
err_free_mem:
|
||||
input_free_device(input);
|
||||
kfree(tsdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit mrstouch_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mrstouch_dev *tsdev = platform_get_drvdata(pdev);
|
||||
|
||||
free_irq(tsdev->irq, tsdev);
|
||||
input_unregister_device(tsdev->input);
|
||||
kfree(tsdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mrstouch_driver = {
|
||||
.driver = {
|
||||
.name = "pmic_touch",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = mrstouch_probe,
|
||||
.remove = __devexit_p(mrstouch_remove),
|
||||
};
|
||||
|
||||
static int __init mrstouch_init(void)
|
||||
{
|
||||
return platform_driver_register(&mrstouch_driver);
|
||||
}
|
||||
module_init(mrstouch_init);
|
||||
|
||||
static void __exit mrstouch_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&mrstouch_driver);
|
||||
}
|
||||
module_exit(mrstouch_exit);
|
||||
|
||||
MODULE_AUTHOR("Sreedhara Murthy. D.S, sreedhara.ds@intel.com");
|
||||
MODULE_DESCRIPTION("Intel Moorestown Resistive Touch Screen Driver");
|
||||
MODULE_LICENSE("GPL");
|
411
drivers/input/touchscreen/lpc32xx_ts.c
Normal file
411
drivers/input/touchscreen/lpc32xx_ts.c
Normal file
@ -0,0 +1,411 @@
|
||||
/*
|
||||
* LPC32xx built-in touchscreen driver
|
||||
*
|
||||
* Copyright (C) 2010 NXP Semiconductors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/*
|
||||
* Touchscreen controller register offsets
|
||||
*/
|
||||
#define LPC32XX_TSC_STAT 0x00
|
||||
#define LPC32XX_TSC_SEL 0x04
|
||||
#define LPC32XX_TSC_CON 0x08
|
||||
#define LPC32XX_TSC_FIFO 0x0C
|
||||
#define LPC32XX_TSC_DTR 0x10
|
||||
#define LPC32XX_TSC_RTR 0x14
|
||||
#define LPC32XX_TSC_UTR 0x18
|
||||
#define LPC32XX_TSC_TTR 0x1C
|
||||
#define LPC32XX_TSC_DXP 0x20
|
||||
#define LPC32XX_TSC_MIN_X 0x24
|
||||
#define LPC32XX_TSC_MAX_X 0x28
|
||||
#define LPC32XX_TSC_MIN_Y 0x2C
|
||||
#define LPC32XX_TSC_MAX_Y 0x30
|
||||
#define LPC32XX_TSC_AUX_UTR 0x34
|
||||
#define LPC32XX_TSC_AUX_MIN 0x38
|
||||
#define LPC32XX_TSC_AUX_MAX 0x3C
|
||||
|
||||
#define LPC32XX_TSC_STAT_FIFO_OVRRN (1 << 8)
|
||||
#define LPC32XX_TSC_STAT_FIFO_EMPTY (1 << 7)
|
||||
|
||||
#define LPC32XX_TSC_SEL_DEFVAL 0x0284
|
||||
|
||||
#define LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 (0x1 << 11)
|
||||
#define LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(s) ((10 - (s)) << 7)
|
||||
#define LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(s) ((10 - (s)) << 4)
|
||||
#define LPC32XX_TSC_ADCCON_POWER_UP (1 << 2)
|
||||
#define LPC32XX_TSC_ADCCON_AUTO_EN (1 << 0)
|
||||
|
||||
#define LPC32XX_TSC_FIFO_TS_P_LEVEL (1 << 31)
|
||||
#define LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(x) (((x) & 0x03FF0000) >> 16)
|
||||
#define LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(y) ((y) & 0x000003FF)
|
||||
|
||||
#define LPC32XX_TSC_ADCDAT_VALUE_MASK 0x000003FF
|
||||
|
||||
#define LPC32XX_TSC_MIN_XY_VAL 0x0
|
||||
#define LPC32XX_TSC_MAX_XY_VAL 0x3FF
|
||||
|
||||
#define MOD_NAME "ts-lpc32xx"
|
||||
|
||||
#define tsc_readl(dev, reg) \
|
||||
__raw_readl((dev)->tsc_base + (reg))
|
||||
#define tsc_writel(dev, reg, val) \
|
||||
__raw_writel((val), (dev)->tsc_base + (reg))
|
||||
|
||||
struct lpc32xx_tsc {
|
||||
struct input_dev *dev;
|
||||
void __iomem *tsc_base;
|
||||
int irq;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static void lpc32xx_fifo_clear(struct lpc32xx_tsc *tsc)
|
||||
{
|
||||
while (!(tsc_readl(tsc, LPC32XX_TSC_STAT) &
|
||||
LPC32XX_TSC_STAT_FIFO_EMPTY))
|
||||
tsc_readl(tsc, LPC32XX_TSC_FIFO);
|
||||
}
|
||||
|
||||
static irqreturn_t lpc32xx_ts_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
u32 tmp, rv[4], xs[4], ys[4];
|
||||
int idx;
|
||||
struct lpc32xx_tsc *tsc = dev_id;
|
||||
struct input_dev *input = tsc->dev;
|
||||
|
||||
tmp = tsc_readl(tsc, LPC32XX_TSC_STAT);
|
||||
|
||||
if (tmp & LPC32XX_TSC_STAT_FIFO_OVRRN) {
|
||||
/* FIFO overflow - throw away samples */
|
||||
lpc32xx_fifo_clear(tsc);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gather and normalize 4 samples. Pen-up events may have less
|
||||
* than 4 samples, but its ok to pop 4 and let the last sample
|
||||
* pen status check drop the samples.
|
||||
*/
|
||||
idx = 0;
|
||||
while (idx < 4 &&
|
||||
!(tsc_readl(tsc, LPC32XX_TSC_STAT) &
|
||||
LPC32XX_TSC_STAT_FIFO_EMPTY)) {
|
||||
tmp = tsc_readl(tsc, LPC32XX_TSC_FIFO);
|
||||
xs[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK -
|
||||
LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(tmp);
|
||||
ys[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK -
|
||||
LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(tmp);
|
||||
rv[idx] = tmp;
|
||||
idx++;
|
||||
}
|
||||
|
||||
/* Data is only valid if pen is still down in last sample */
|
||||
if (!(rv[3] & LPC32XX_TSC_FIFO_TS_P_LEVEL) && idx == 4) {
|
||||
/* Use average of 2nd and 3rd sample for position */
|
||||
input_report_abs(input, ABS_X, (xs[1] + xs[2]) / 2);
|
||||
input_report_abs(input, ABS_Y, (ys[1] + ys[2]) / 2);
|
||||
input_report_key(input, BTN_TOUCH, 1);
|
||||
} else {
|
||||
input_report_key(input, BTN_TOUCH, 0);
|
||||
}
|
||||
|
||||
input_sync(input);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void lpc32xx_stop_tsc(struct lpc32xx_tsc *tsc)
|
||||
{
|
||||
/* Disable auto mode */
|
||||
tsc_writel(tsc, LPC32XX_TSC_CON,
|
||||
tsc_readl(tsc, LPC32XX_TSC_CON) &
|
||||
~LPC32XX_TSC_ADCCON_AUTO_EN);
|
||||
|
||||
clk_disable(tsc->clk);
|
||||
}
|
||||
|
||||
static void lpc32xx_setup_tsc(struct lpc32xx_tsc *tsc)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
clk_enable(tsc->clk);
|
||||
|
||||
tmp = tsc_readl(tsc, LPC32XX_TSC_CON) & ~LPC32XX_TSC_ADCCON_POWER_UP;
|
||||
|
||||
/* Set the TSC FIFO depth to 4 samples @ 10-bits per sample (max) */
|
||||
tmp = LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 |
|
||||
LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(10) |
|
||||
LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(10);
|
||||
tsc_writel(tsc, LPC32XX_TSC_CON, tmp);
|
||||
|
||||
/* These values are all preset */
|
||||
tsc_writel(tsc, LPC32XX_TSC_SEL, LPC32XX_TSC_SEL_DEFVAL);
|
||||
tsc_writel(tsc, LPC32XX_TSC_MIN_X, LPC32XX_TSC_MIN_XY_VAL);
|
||||
tsc_writel(tsc, LPC32XX_TSC_MAX_X, LPC32XX_TSC_MAX_XY_VAL);
|
||||
tsc_writel(tsc, LPC32XX_TSC_MIN_Y, LPC32XX_TSC_MIN_XY_VAL);
|
||||
tsc_writel(tsc, LPC32XX_TSC_MAX_Y, LPC32XX_TSC_MAX_XY_VAL);
|
||||
|
||||
/* Aux support is not used */
|
||||
tsc_writel(tsc, LPC32XX_TSC_AUX_UTR, 0);
|
||||
tsc_writel(tsc, LPC32XX_TSC_AUX_MIN, 0);
|
||||
tsc_writel(tsc, LPC32XX_TSC_AUX_MAX, 0);
|
||||
|
||||
/*
|
||||
* Set sample rate to about 240Hz per X/Y pair. A single measurement
|
||||
* consists of 4 pairs which gives about a 60Hz sample rate based on
|
||||
* a stable 32768Hz clock source. Values are in clocks.
|
||||
* Rate is (32768 / (RTR + XCONV + RTR + YCONV + DXP + TTR + UTR) / 4
|
||||
*/
|
||||
tsc_writel(tsc, LPC32XX_TSC_RTR, 0x2);
|
||||
tsc_writel(tsc, LPC32XX_TSC_DTR, 0x2);
|
||||
tsc_writel(tsc, LPC32XX_TSC_TTR, 0x10);
|
||||
tsc_writel(tsc, LPC32XX_TSC_DXP, 0x4);
|
||||
tsc_writel(tsc, LPC32XX_TSC_UTR, 88);
|
||||
|
||||
lpc32xx_fifo_clear(tsc);
|
||||
|
||||
/* Enable automatic ts event capture */
|
||||
tsc_writel(tsc, LPC32XX_TSC_CON, tmp | LPC32XX_TSC_ADCCON_AUTO_EN);
|
||||
}
|
||||
|
||||
static int lpc32xx_ts_open(struct input_dev *dev)
|
||||
{
|
||||
struct lpc32xx_tsc *tsc = input_get_drvdata(dev);
|
||||
|
||||
lpc32xx_setup_tsc(tsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lpc32xx_ts_close(struct input_dev *dev)
|
||||
{
|
||||
struct lpc32xx_tsc *tsc = input_get_drvdata(dev);
|
||||
|
||||
lpc32xx_stop_tsc(tsc);
|
||||
}
|
||||
|
||||
static int __devinit lpc32xx_ts_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct lpc32xx_tsc *tsc;
|
||||
struct input_dev *input;
|
||||
struct resource *res;
|
||||
resource_size_t size;
|
||||
int irq;
|
||||
int error;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Can't get memory resource\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "Can't get interrupt resource\n");
|
||||
return irq;
|
||||
}
|
||||
|
||||
tsc = kzalloc(sizeof(*tsc), GFP_KERNEL);
|
||||
input = input_allocate_device();
|
||||
if (!tsc || !input) {
|
||||
dev_err(&pdev->dev, "failed allocating memory\n");
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
tsc->dev = input;
|
||||
tsc->irq = irq;
|
||||
|
||||
size = resource_size(res);
|
||||
|
||||
if (!request_mem_region(res->start, size, pdev->name)) {
|
||||
dev_err(&pdev->dev, "TSC registers are not free\n");
|
||||
error = -EBUSY;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
tsc->tsc_base = ioremap(res->start, size);
|
||||
if (!tsc->tsc_base) {
|
||||
dev_err(&pdev->dev, "Can't map memory\n");
|
||||
error = -ENOMEM;
|
||||
goto err_release_mem;
|
||||
}
|
||||
|
||||
tsc->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(tsc->clk)) {
|
||||
dev_err(&pdev->dev, "failed getting clock\n");
|
||||
error = PTR_ERR(tsc->clk);
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
input->name = MOD_NAME;
|
||||
input->phys = "lpc32xx/input0";
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->id.vendor = 0x0001;
|
||||
input->id.product = 0x0002;
|
||||
input->id.version = 0x0100;
|
||||
input->dev.parent = &pdev->dev;
|
||||
input->open = lpc32xx_ts_open;
|
||||
input->close = lpc32xx_ts_close;
|
||||
|
||||
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||
input_set_abs_params(input, ABS_X, LPC32XX_TSC_MIN_XY_VAL,
|
||||
LPC32XX_TSC_MAX_XY_VAL, 0, 0);
|
||||
input_set_abs_params(input, ABS_Y, LPC32XX_TSC_MIN_XY_VAL,
|
||||
LPC32XX_TSC_MAX_XY_VAL, 0, 0);
|
||||
|
||||
input_set_drvdata(input, tsc);
|
||||
|
||||
error = request_irq(tsc->irq, lpc32xx_ts_interrupt,
|
||||
IRQF_DISABLED, pdev->name, tsc);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed requesting interrupt\n");
|
||||
goto err_put_clock;
|
||||
}
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed registering input device\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, tsc);
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(tsc->irq, tsc);
|
||||
err_put_clock:
|
||||
clk_put(tsc->clk);
|
||||
err_unmap:
|
||||
iounmap(tsc->tsc_base);
|
||||
err_release_mem:
|
||||
release_mem_region(res->start, size);
|
||||
err_free_mem:
|
||||
input_free_device(input);
|
||||
kfree(tsc);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit lpc32xx_ts_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct lpc32xx_tsc *tsc = platform_get_drvdata(pdev);
|
||||
struct resource *res;
|
||||
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
free_irq(tsc->irq, tsc);
|
||||
|
||||
input_unregister_device(tsc->dev);
|
||||
|
||||
clk_put(tsc->clk);
|
||||
|
||||
iounmap(tsc->tsc_base);
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
|
||||
kfree(tsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int lpc32xx_ts_suspend(struct device *dev)
|
||||
{
|
||||
struct lpc32xx_tsc *tsc = dev_get_drvdata(dev);
|
||||
struct input_dev *input = tsc->dev;
|
||||
|
||||
/*
|
||||
* Suspend and resume can be called when the device hasn't been
|
||||
* enabled. If there are no users that have the device open, then
|
||||
* avoid calling the TSC stop and start functions as the TSC
|
||||
* isn't yet clocked.
|
||||
*/
|
||||
mutex_lock(&input->mutex);
|
||||
|
||||
if (input->users) {
|
||||
if (device_may_wakeup(dev))
|
||||
enable_irq_wake(tsc->irq);
|
||||
else
|
||||
lpc32xx_stop_tsc(tsc);
|
||||
}
|
||||
|
||||
mutex_unlock(&input->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lpc32xx_ts_resume(struct device *dev)
|
||||
{
|
||||
struct lpc32xx_tsc *tsc = dev_get_drvdata(dev);
|
||||
struct input_dev *input = tsc->dev;
|
||||
|
||||
mutex_lock(&input->mutex);
|
||||
|
||||
if (input->users) {
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(tsc->irq);
|
||||
else
|
||||
lpc32xx_setup_tsc(tsc);
|
||||
}
|
||||
|
||||
mutex_unlock(&input->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops lpc32xx_ts_pm_ops = {
|
||||
.suspend = lpc32xx_ts_suspend,
|
||||
.resume = lpc32xx_ts_resume,
|
||||
};
|
||||
#define LPC32XX_TS_PM_OPS (&lpc32xx_ts_pm_ops)
|
||||
#else
|
||||
#define LPC32XX_TS_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver lpc32xx_ts_driver = {
|
||||
.probe = lpc32xx_ts_probe,
|
||||
.remove = __devexit_p(lpc32xx_ts_remove),
|
||||
.driver = {
|
||||
.name = MOD_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = LPC32XX_TS_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init lpc32xx_ts_init(void)
|
||||
{
|
||||
return platform_driver_register(&lpc32xx_ts_driver);
|
||||
}
|
||||
module_init(lpc32xx_ts_init);
|
||||
|
||||
static void __exit lpc32xx_ts_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&lpc32xx_ts_driver);
|
||||
}
|
||||
module_exit(lpc32xx_ts_exit);
|
||||
|
||||
MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com");
|
||||
MODULE_DESCRIPTION("LPC32XX TSC Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:lpc32xx_ts");
|
@ -350,7 +350,7 @@ static int __devinit s3c2410ts_probe(struct platform_device *pdev)
|
||||
err_tcirq:
|
||||
free_irq(ts.irq_tc, ts.input);
|
||||
err_inputdev:
|
||||
input_unregister_device(ts.input);
|
||||
input_free_device(ts.input);
|
||||
err_iomap:
|
||||
iounmap(ts.io);
|
||||
err_clk:
|
||||
|
@ -268,7 +268,7 @@ static int __devinit stmpe_input_probe(struct platform_device *pdev)
|
||||
struct stmpe_touch *ts;
|
||||
struct input_dev *idev;
|
||||
struct stmpe_ts_platform_data *ts_pdata = NULL;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
int ts_irq;
|
||||
|
||||
ts_irq = platform_get_irq_byname(pdev, "FIFO_TH");
|
||||
@ -276,12 +276,16 @@ static int __devinit stmpe_input_probe(struct platform_device *pdev)
|
||||
return ts_irq;
|
||||
|
||||
ts = kzalloc(sizeof(*ts), GFP_KERNEL);
|
||||
if (!ts)
|
||||
if (!ts) {
|
||||
ret = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
idev = input_allocate_device();
|
||||
if (!idev)
|
||||
if (!idev) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_ts;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ts);
|
||||
ts->stmpe = stmpe;
|
||||
@ -361,7 +365,6 @@ static int __devexit stmpe_ts_remove(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
input_unregister_device(ts->idev);
|
||||
input_free_device(ts->idev);
|
||||
|
||||
kfree(ts);
|
||||
|
||||
|
@ -335,6 +335,7 @@ static int tps6507x_ts_probe(struct platform_device *pdev)
|
||||
dev_err(tsc->dev, "schedule failed");
|
||||
goto err2;
|
||||
}
|
||||
platform_set_drvdata(pdev, tps6507x_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -358,7 +359,7 @@ static int __devexit tps6507x_ts_remove(struct platform_device *pdev)
|
||||
cancel_delayed_work_sync(&tsc->work);
|
||||
destroy_workqueue(tsc->wq);
|
||||
|
||||
input_free_device(input_dev);
|
||||
input_unregister_device(input_dev);
|
||||
|
||||
tps6507x_dev->ts = NULL;
|
||||
kfree(tsc);
|
||||
|
@ -265,7 +265,7 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct tsc2007 *ts;
|
||||
struct tsc2007_platform_data *pdata = pdata = client->dev.platform_data;
|
||||
struct tsc2007_platform_data *pdata = client->dev.platform_data;
|
||||
struct input_dev *input_dev;
|
||||
int err;
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
* Wacom W8001 penabled serial touchscreen driver
|
||||
*
|
||||
* Copyright (c) 2008 Jaya Kumar
|
||||
* Copyright (c) 2010 Red Hat, Inc.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file COPYING in the main directory of this archive for
|
||||
@ -30,11 +31,24 @@ MODULE_LICENSE("GPL");
|
||||
#define W8001_LEAD_BYTE 0x80
|
||||
#define W8001_TAB_MASK 0x40
|
||||
#define W8001_TAB_BYTE 0x40
|
||||
/* set in first byte of touch data packets */
|
||||
#define W8001_TOUCH_MASK (0x10 | W8001_LEAD_MASK)
|
||||
#define W8001_TOUCH_BYTE (0x10 | W8001_LEAD_BYTE)
|
||||
|
||||
#define W8001_QUERY_PACKET 0x20
|
||||
|
||||
#define W8001_CMD_START '1'
|
||||
#define W8001_CMD_QUERY '*'
|
||||
#define W8001_CMD_TOUCHQUERY '%'
|
||||
|
||||
/* length of data packets in bytes, depends on device. */
|
||||
#define W8001_PKTLEN_TOUCH93 5
|
||||
#define W8001_PKTLEN_TOUCH9A 7
|
||||
#define W8001_PKTLEN_TPCPEN 9
|
||||
#define W8001_PKTLEN_TPCCTL 11 /* control packet */
|
||||
#define W8001_PKTLEN_TOUCH2FG 13
|
||||
|
||||
#define MAX_TRACKING_ID 0xFF /* arbitrarily chosen */
|
||||
|
||||
struct w8001_coord {
|
||||
u8 rdy;
|
||||
@ -48,6 +62,15 @@ struct w8001_coord {
|
||||
u8 tilt_y;
|
||||
};
|
||||
|
||||
/* touch query reply packet */
|
||||
struct w8001_touch_query {
|
||||
u8 panel_res;
|
||||
u8 capacity_res;
|
||||
u8 sensor_id;
|
||||
u16 x;
|
||||
u16 y;
|
||||
};
|
||||
|
||||
/*
|
||||
* Per-touchscreen data.
|
||||
*/
|
||||
@ -62,6 +85,9 @@ struct w8001 {
|
||||
unsigned char response[W8001_MAX_LENGTH];
|
||||
unsigned char data[W8001_MAX_LENGTH];
|
||||
char phys[32];
|
||||
int type;
|
||||
unsigned int pktlen;
|
||||
int trkid[2];
|
||||
};
|
||||
|
||||
static void parse_data(u8 *data, struct w8001_coord *coord)
|
||||
@ -88,11 +114,98 @@ static void parse_data(u8 *data, struct w8001_coord *coord)
|
||||
coord->tilt_y = data[8] & 0x7F;
|
||||
}
|
||||
|
||||
static void parse_touch(struct w8001 *w8001)
|
||||
{
|
||||
static int trkid;
|
||||
struct input_dev *dev = w8001->dev;
|
||||
unsigned char *data = w8001->data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
input_mt_slot(dev, i);
|
||||
|
||||
if (data[0] & (1 << i)) {
|
||||
int x = (data[6 * i + 1] << 7) | (data[6 * i + 2]);
|
||||
int y = (data[6 * i + 3] << 7) | (data[6 * i + 4]);
|
||||
/* data[5,6] and [11,12] is finger capacity */
|
||||
|
||||
input_report_abs(dev, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(dev, ABS_MT_POSITION_Y, y);
|
||||
input_report_abs(dev, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
|
||||
if (w8001->trkid[i] < 0)
|
||||
w8001->trkid[i] = trkid++ & MAX_TRACKING_ID;
|
||||
} else {
|
||||
w8001->trkid[i] = -1;
|
||||
}
|
||||
input_report_abs(dev, ABS_MT_TRACKING_ID, w8001->trkid[i]);
|
||||
}
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
static void parse_touchquery(u8 *data, struct w8001_touch_query *query)
|
||||
{
|
||||
memset(query, 0, sizeof(*query));
|
||||
|
||||
query->panel_res = data[1];
|
||||
query->sensor_id = data[2] & 0x7;
|
||||
query->capacity_res = data[7];
|
||||
|
||||
query->x = data[3] << 9;
|
||||
query->x |= data[4] << 2;
|
||||
query->x |= (data[2] >> 5) & 0x3;
|
||||
|
||||
query->y = data[5] << 9;
|
||||
query->y |= data[6] << 2;
|
||||
query->y |= (data[2] >> 3) & 0x3;
|
||||
}
|
||||
|
||||
static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
|
||||
{
|
||||
struct input_dev *dev = w8001->dev;
|
||||
|
||||
/*
|
||||
* We have 1 bit for proximity (rdy) and 3 bits for tip, side,
|
||||
* side2/eraser. If rdy && f2 are set, this can be either pen + side2,
|
||||
* or eraser. assume
|
||||
* - if dev is already in proximity and f2 is toggled → pen + side2
|
||||
* - if dev comes into proximity with f2 set → eraser
|
||||
* If f2 disappears after assuming eraser, fake proximity out for
|
||||
* eraser and in for pen.
|
||||
*/
|
||||
|
||||
if (!w8001->type) {
|
||||
w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
|
||||
} else if (w8001->type == BTN_TOOL_RUBBER) {
|
||||
if (!coord->f2) {
|
||||
input_report_abs(dev, ABS_PRESSURE, 0);
|
||||
input_report_key(dev, BTN_TOUCH, 0);
|
||||
input_report_key(dev, BTN_STYLUS, 0);
|
||||
input_report_key(dev, BTN_STYLUS2, 0);
|
||||
input_report_key(dev, BTN_TOOL_RUBBER, 0);
|
||||
input_sync(dev);
|
||||
w8001->type = BTN_TOOL_PEN;
|
||||
}
|
||||
} else {
|
||||
input_report_key(dev, BTN_STYLUS2, coord->f2);
|
||||
}
|
||||
|
||||
input_report_abs(dev, ABS_X, coord->x);
|
||||
input_report_abs(dev, ABS_Y, coord->y);
|
||||
input_report_abs(dev, ABS_PRESSURE, coord->pen_pressure);
|
||||
input_report_key(dev, BTN_TOUCH, coord->tsw);
|
||||
input_report_key(dev, BTN_STYLUS, coord->f1);
|
||||
input_report_key(dev, w8001->type, coord->rdy);
|
||||
input_sync(dev);
|
||||
|
||||
if (!coord->rdy)
|
||||
w8001->type = 0;
|
||||
}
|
||||
|
||||
static irqreturn_t w8001_interrupt(struct serio *serio,
|
||||
unsigned char data, unsigned int flags)
|
||||
{
|
||||
struct w8001 *w8001 = serio_get_drvdata(serio);
|
||||
struct input_dev *dev = w8001->dev;
|
||||
struct w8001_coord coord;
|
||||
unsigned char tmp;
|
||||
|
||||
@ -105,26 +218,45 @@ static irqreturn_t w8001_interrupt(struct serio *serio,
|
||||
}
|
||||
break;
|
||||
|
||||
case 8:
|
||||
case W8001_PKTLEN_TOUCH93 - 1:
|
||||
case W8001_PKTLEN_TOUCH9A - 1:
|
||||
/* ignore one-finger touch packet. */
|
||||
if (w8001->pktlen == w8001->idx)
|
||||
w8001->idx = 0;
|
||||
break;
|
||||
|
||||
/* Pen coordinates packet */
|
||||
case W8001_PKTLEN_TPCPEN - 1:
|
||||
tmp = w8001->data[0] & W8001_TAB_MASK;
|
||||
if (unlikely(tmp == W8001_TAB_BYTE))
|
||||
break;
|
||||
|
||||
tmp = (w8001->data[0] & W8001_TOUCH_BYTE);
|
||||
if (tmp == W8001_TOUCH_BYTE)
|
||||
break;
|
||||
|
||||
w8001->idx = 0;
|
||||
parse_data(w8001->data, &coord);
|
||||
input_report_abs(dev, ABS_X, coord.x);
|
||||
input_report_abs(dev, ABS_Y, coord.y);
|
||||
input_report_abs(dev, ABS_PRESSURE, coord.pen_pressure);
|
||||
input_report_key(dev, BTN_TOUCH, coord.tsw);
|
||||
input_sync(dev);
|
||||
report_pen_events(w8001, &coord);
|
||||
break;
|
||||
|
||||
case 10:
|
||||
/* control packet */
|
||||
case W8001_PKTLEN_TPCCTL - 1:
|
||||
tmp = (w8001->data[0] & W8001_TOUCH_MASK);
|
||||
if (tmp == W8001_TOUCH_BYTE)
|
||||
break;
|
||||
|
||||
w8001->idx = 0;
|
||||
memcpy(w8001->response, w8001->data, W8001_MAX_LENGTH);
|
||||
w8001->response_type = W8001_QUERY_PACKET;
|
||||
complete(&w8001->cmd_done);
|
||||
break;
|
||||
|
||||
/* 2 finger touch packet */
|
||||
case W8001_PKTLEN_TOUCH2FG - 1:
|
||||
w8001->idx = 0;
|
||||
parse_touch(w8001);
|
||||
break;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
@ -167,6 +299,38 @@ static int w8001_setup(struct w8001 *w8001)
|
||||
input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
|
||||
|
||||
error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true);
|
||||
if (!error) {
|
||||
struct w8001_touch_query touch;
|
||||
|
||||
parse_touchquery(w8001->response, &touch);
|
||||
|
||||
switch (touch.sensor_id) {
|
||||
case 0:
|
||||
case 2:
|
||||
w8001->pktlen = W8001_PKTLEN_TOUCH93;
|
||||
break;
|
||||
case 1:
|
||||
case 3:
|
||||
case 4:
|
||||
w8001->pktlen = W8001_PKTLEN_TOUCH9A;
|
||||
break;
|
||||
case 5:
|
||||
w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
|
||||
|
||||
input_mt_create_slots(dev, 2);
|
||||
input_set_abs_params(dev, ABS_MT_TRACKING_ID,
|
||||
0, MAX_TRACKING_ID, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_X,
|
||||
0, touch.x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_Y,
|
||||
0, touch.y, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_TOOL_TYPE,
|
||||
0, 0, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return w8001_command(w8001, W8001_CMD_START, false);
|
||||
}
|
||||
|
||||
@ -208,6 +372,7 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
|
||||
w8001->serio = serio;
|
||||
w8001->id = serio->id.id;
|
||||
w8001->dev = input_dev;
|
||||
w8001->trkid[0] = w8001->trkid[1] = -1;
|
||||
init_completion(&w8001->cmd_done);
|
||||
snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys);
|
||||
|
||||
@ -221,6 +386,10 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
|
||||
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||
input_dev->keybit[BIT_WORD(BTN_TOOL_PEN)] |= BIT_MASK(BTN_TOOL_PEN);
|
||||
input_dev->keybit[BIT_WORD(BTN_TOOL_RUBBER)] |= BIT_MASK(BTN_TOOL_RUBBER);
|
||||
input_dev->keybit[BIT_WORD(BTN_STYLUS)] |= BIT_MASK(BTN_STYLUS);
|
||||
input_dev->keybit[BIT_WORD(BTN_STYLUS2)] |= BIT_MASK(BTN_STYLUS2);
|
||||
|
||||
serio_set_drvdata(serio, w8001);
|
||||
err = serio_open(serio, drv);
|
||||
|
@ -125,6 +125,8 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel)
|
||||
{
|
||||
int power_adc = 0, auxval;
|
||||
u16 power = 0;
|
||||
int rc = 0;
|
||||
int timeout = 0;
|
||||
|
||||
/* get codec */
|
||||
mutex_lock(&wm->codec_mutex);
|
||||
@ -143,7 +145,9 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel)
|
||||
|
||||
/* Turn polling mode on to read AUX ADC */
|
||||
wm->pen_probably_down = 1;
|
||||
wm->codec->poll_sample(wm, adcsel, &auxval);
|
||||
|
||||
while (rc != RC_VALID && timeout++ < 5)
|
||||
rc = wm->codec->poll_sample(wm, adcsel, &auxval);
|
||||
|
||||
if (power_adc)
|
||||
wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000);
|
||||
@ -152,8 +156,15 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel)
|
||||
|
||||
wm->pen_probably_down = 0;
|
||||
|
||||
if (timeout >= 5) {
|
||||
dev_err(wm->dev,
|
||||
"timeout reading auxadc %d, disabling digitiser\n",
|
||||
adcsel);
|
||||
wm->codec->dig_enable(wm, false);
|
||||
}
|
||||
|
||||
mutex_unlock(&wm->codec_mutex);
|
||||
return auxval & 0xfff;
|
||||
return (rc == RC_VALID ? auxval & 0xfff : -EBUSY);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc);
|
||||
|
||||
@ -684,8 +695,7 @@ static int wm97xx_probe(struct device *dev)
|
||||
touch_reg_err:
|
||||
platform_device_put(wm->touch_dev);
|
||||
touch_err:
|
||||
platform_device_unregister(wm->battery_dev);
|
||||
wm->battery_dev = NULL;
|
||||
platform_device_del(wm->battery_dev);
|
||||
batt_reg_err:
|
||||
platform_device_put(wm->battery_dev);
|
||||
batt_err:
|
||||
|
@ -24,15 +24,57 @@
|
||||
/* FIXME: IR_KEYPRESS_TIMEOUT should be protocol specific */
|
||||
#define IR_KEYPRESS_TIMEOUT 250
|
||||
|
||||
/**
|
||||
* ir_create_table() - initializes a scancode table
|
||||
* @rc_tab: the ir_scancode_table to initialize
|
||||
* @name: name to assign to the table
|
||||
* @ir_type: ir type to assign to the new table
|
||||
* @size: initial size of the table
|
||||
* @return: zero on success or a negative error code
|
||||
*
|
||||
* This routine will initialize the ir_scancode_table and will allocate
|
||||
* memory to hold at least the specified number elements.
|
||||
*/
|
||||
static int ir_create_table(struct ir_scancode_table *rc_tab,
|
||||
const char *name, u64 ir_type, size_t size)
|
||||
{
|
||||
rc_tab->name = name;
|
||||
rc_tab->ir_type = ir_type;
|
||||
rc_tab->alloc = roundup_pow_of_two(size * sizeof(struct ir_scancode));
|
||||
rc_tab->size = rc_tab->alloc / sizeof(struct ir_scancode);
|
||||
rc_tab->scan = kmalloc(rc_tab->alloc, GFP_KERNEL);
|
||||
if (!rc_tab->scan)
|
||||
return -ENOMEM;
|
||||
|
||||
IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n",
|
||||
rc_tab->size, rc_tab->alloc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ir_free_table() - frees memory allocated by a scancode table
|
||||
* @rc_tab: the table whose mappings need to be freed
|
||||
*
|
||||
* This routine will free memory alloctaed for key mappings used by given
|
||||
* scancode table.
|
||||
*/
|
||||
static void ir_free_table(struct ir_scancode_table *rc_tab)
|
||||
{
|
||||
rc_tab->size = 0;
|
||||
kfree(rc_tab->scan);
|
||||
rc_tab->scan = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ir_resize_table() - resizes a scancode table if necessary
|
||||
* @rc_tab: the ir_scancode_table to resize
|
||||
* @gfp_flags: gfp flags to use when allocating memory
|
||||
* @return: zero on success or a negative error code
|
||||
*
|
||||
* This routine will shrink the ir_scancode_table if it has lots of
|
||||
* unused entries and grow it if it is full.
|
||||
*/
|
||||
static int ir_resize_table(struct ir_scancode_table *rc_tab)
|
||||
static int ir_resize_table(struct ir_scancode_table *rc_tab, gfp_t gfp_flags)
|
||||
{
|
||||
unsigned int oldalloc = rc_tab->alloc;
|
||||
unsigned int newalloc = oldalloc;
|
||||
@ -57,7 +99,7 @@ static int ir_resize_table(struct ir_scancode_table *rc_tab)
|
||||
if (newalloc == oldalloc)
|
||||
return 0;
|
||||
|
||||
newscan = kmalloc(newalloc, GFP_ATOMIC);
|
||||
newscan = kmalloc(newalloc, gfp_flags);
|
||||
if (!newscan) {
|
||||
IR_dprintk(1, "Failed to kmalloc %u bytes\n", newalloc);
|
||||
return -ENOMEM;
|
||||
@ -72,26 +114,78 @@ static int ir_resize_table(struct ir_scancode_table *rc_tab)
|
||||
}
|
||||
|
||||
/**
|
||||
* ir_do_setkeycode() - internal function to set a keycode in the
|
||||
* scancode->keycode table
|
||||
* ir_update_mapping() - set a keycode in the scancode->keycode table
|
||||
* @dev: the struct input_dev device descriptor
|
||||
* @rc_tab: the struct ir_scancode_table to set the keycode in
|
||||
* @scancode: the scancode for the ir command
|
||||
* @keycode: the keycode for the ir command
|
||||
* @resize: whether the keytable may be shrunk
|
||||
* @return: -EINVAL if the keycode could not be inserted, otherwise zero.
|
||||
* @rc_tab: scancode table to be adjusted
|
||||
* @index: index of the mapping that needs to be updated
|
||||
* @keycode: the desired keycode
|
||||
* @return: previous keycode assigned to the mapping
|
||||
*
|
||||
* This routine is used internally to manipulate the scancode->keycode table.
|
||||
* The caller has to hold @rc_tab->lock.
|
||||
* This routine is used to update scancode->keycopde mapping at given
|
||||
* position.
|
||||
*/
|
||||
static int ir_do_setkeycode(struct input_dev *dev,
|
||||
struct ir_scancode_table *rc_tab,
|
||||
unsigned scancode, unsigned keycode,
|
||||
bool resize)
|
||||
static unsigned int ir_update_mapping(struct input_dev *dev,
|
||||
struct ir_scancode_table *rc_tab,
|
||||
unsigned int index,
|
||||
unsigned int new_keycode)
|
||||
{
|
||||
int old_keycode = rc_tab->scan[index].keycode;
|
||||
int i;
|
||||
|
||||
/* Did the user wish to remove the mapping? */
|
||||
if (new_keycode == KEY_RESERVED || new_keycode == KEY_UNKNOWN) {
|
||||
IR_dprintk(1, "#%d: Deleting scan 0x%04x\n",
|
||||
index, rc_tab->scan[index].scancode);
|
||||
rc_tab->len--;
|
||||
memmove(&rc_tab->scan[index], &rc_tab->scan[index+ 1],
|
||||
(rc_tab->len - index) * sizeof(struct ir_scancode));
|
||||
} else {
|
||||
IR_dprintk(1, "#%d: %s scan 0x%04x with key 0x%04x\n",
|
||||
index,
|
||||
old_keycode == KEY_RESERVED ? "New" : "Replacing",
|
||||
rc_tab->scan[index].scancode, new_keycode);
|
||||
rc_tab->scan[index].keycode = new_keycode;
|
||||
__set_bit(new_keycode, dev->keybit);
|
||||
}
|
||||
|
||||
if (old_keycode != KEY_RESERVED) {
|
||||
/* A previous mapping was updated... */
|
||||
__clear_bit(old_keycode, dev->keybit);
|
||||
/* ... but another scancode might use the same keycode */
|
||||
for (i = 0; i < rc_tab->len; i++) {
|
||||
if (rc_tab->scan[i].keycode == old_keycode) {
|
||||
__set_bit(old_keycode, dev->keybit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Possibly shrink the keytable, failure is not a problem */
|
||||
ir_resize_table(rc_tab, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
return old_keycode;
|
||||
}
|
||||
|
||||
/**
|
||||
* ir_locate_scancode() - set a keycode in the scancode->keycode table
|
||||
* @ir_dev: the struct ir_input_dev device descriptor
|
||||
* @rc_tab: scancode table to be searched
|
||||
* @scancode: the desired scancode
|
||||
* @resize: controls whether we allowed to resize the table to
|
||||
* accomodate not yet present scancodes
|
||||
* @return: index of the mapping containing scancode in question
|
||||
* or -1U in case of failure.
|
||||
*
|
||||
* This routine is used to locate given scancode in ir_scancode_table.
|
||||
* If scancode is not yet present the routine will allocate a new slot
|
||||
* for it.
|
||||
*/
|
||||
static unsigned int ir_establish_scancode(struct ir_input_dev *ir_dev,
|
||||
struct ir_scancode_table *rc_tab,
|
||||
unsigned int scancode,
|
||||
bool resize)
|
||||
{
|
||||
unsigned int i;
|
||||
int old_keycode = KEY_RESERVED;
|
||||
struct ir_input_dev *ir_dev = input_get_drvdata(dev);
|
||||
|
||||
/*
|
||||
* Unfortunately, some hardware-based IR decoders don't provide
|
||||
@ -100,65 +194,34 @@ static int ir_do_setkeycode(struct input_dev *dev,
|
||||
* the provided IR with another one, it is needed to allow loading
|
||||
* IR tables from other remotes. So,
|
||||
*/
|
||||
if (ir_dev->props && ir_dev->props->scanmask) {
|
||||
if (ir_dev->props && ir_dev->props->scanmask)
|
||||
scancode &= ir_dev->props->scanmask;
|
||||
}
|
||||
|
||||
/* First check if we already have a mapping for this ir command */
|
||||
for (i = 0; i < rc_tab->len; i++) {
|
||||
if (rc_tab->scan[i].scancode == scancode)
|
||||
return i;
|
||||
|
||||
/* Keytable is sorted from lowest to highest scancode */
|
||||
if (rc_tab->scan[i].scancode > scancode)
|
||||
if (rc_tab->scan[i].scancode >= scancode)
|
||||
break;
|
||||
else if (rc_tab->scan[i].scancode < scancode)
|
||||
continue;
|
||||
|
||||
old_keycode = rc_tab->scan[i].keycode;
|
||||
rc_tab->scan[i].keycode = keycode;
|
||||
|
||||
/* Did the user wish to remove the mapping? */
|
||||
if (keycode == KEY_RESERVED || keycode == KEY_UNKNOWN) {
|
||||
IR_dprintk(1, "#%d: Deleting scan 0x%04x\n",
|
||||
i, scancode);
|
||||
rc_tab->len--;
|
||||
memmove(&rc_tab->scan[i], &rc_tab->scan[i + 1],
|
||||
(rc_tab->len - i) * sizeof(struct ir_scancode));
|
||||
}
|
||||
|
||||
/* Possibly shrink the keytable, failure is not a problem */
|
||||
ir_resize_table(rc_tab);
|
||||
break;
|
||||
}
|
||||
|
||||
if (old_keycode == KEY_RESERVED && keycode != KEY_RESERVED) {
|
||||
/* No previous mapping found, we might need to grow the table */
|
||||
if (resize && ir_resize_table(rc_tab))
|
||||
return -ENOMEM;
|
||||
/* No previous mapping found, we might need to grow the table */
|
||||
if (rc_tab->size == rc_tab->len) {
|
||||
if (!resize || ir_resize_table(rc_tab, GFP_ATOMIC))
|
||||
return -1U;
|
||||
}
|
||||
|
||||
IR_dprintk(1, "#%d: New scan 0x%04x with key 0x%04x\n",
|
||||
i, scancode, keycode);
|
||||
|
||||
/* i is the proper index to insert our new keycode */
|
||||
/* i is the proper index to insert our new keycode */
|
||||
if (i < rc_tab->len)
|
||||
memmove(&rc_tab->scan[i + 1], &rc_tab->scan[i],
|
||||
(rc_tab->len - i) * sizeof(struct ir_scancode));
|
||||
rc_tab->scan[i].scancode = scancode;
|
||||
rc_tab->scan[i].keycode = keycode;
|
||||
rc_tab->len++;
|
||||
set_bit(keycode, dev->keybit);
|
||||
} else {
|
||||
IR_dprintk(1, "#%d: Replacing scan 0x%04x with key 0x%04x\n",
|
||||
i, scancode, keycode);
|
||||
/* A previous mapping was updated... */
|
||||
clear_bit(old_keycode, dev->keybit);
|
||||
/* ...but another scancode might use the same keycode */
|
||||
for (i = 0; i < rc_tab->len; i++) {
|
||||
if (rc_tab->scan[i].keycode == old_keycode) {
|
||||
set_bit(old_keycode, dev->keybit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
rc_tab->scan[i].scancode = scancode;
|
||||
rc_tab->scan[i].keycode = KEY_RESERVED;
|
||||
rc_tab->len++;
|
||||
|
||||
return 0;
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -171,17 +234,41 @@ static int ir_do_setkeycode(struct input_dev *dev,
|
||||
* This routine is used to handle evdev EVIOCSKEY ioctl.
|
||||
*/
|
||||
static int ir_setkeycode(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int keycode)
|
||||
const struct input_keymap_entry *ke,
|
||||
unsigned int *old_keycode)
|
||||
{
|
||||
int rc;
|
||||
unsigned long flags;
|
||||
struct ir_input_dev *ir_dev = input_get_drvdata(dev);
|
||||
struct ir_scancode_table *rc_tab = &ir_dev->rc_tab;
|
||||
unsigned int index;
|
||||
unsigned int scancode;
|
||||
int retval;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&rc_tab->lock, flags);
|
||||
rc = ir_do_setkeycode(dev, rc_tab, scancode, keycode, true);
|
||||
|
||||
if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
|
||||
index = ke->index;
|
||||
if (index >= rc_tab->len) {
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
retval = input_scancode_to_scalar(ke, &scancode);
|
||||
if (retval)
|
||||
goto out;
|
||||
|
||||
index = ir_establish_scancode(ir_dev, rc_tab, scancode, true);
|
||||
if (index >= rc_tab->len) {
|
||||
retval = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
*old_keycode = ir_update_mapping(dev, rc_tab, index, ke->keycode);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&rc_tab->lock, flags);
|
||||
return rc;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -189,31 +276,72 @@ static int ir_setkeycode(struct input_dev *dev,
|
||||
* @dev: the struct input_dev device descriptor
|
||||
* @to: the struct ir_scancode_table to copy entries to
|
||||
* @from: the struct ir_scancode_table to copy entries from
|
||||
* @return: -EINVAL if all keycodes could not be inserted, otherwise zero.
|
||||
* @return: -ENOMEM if all keycodes could not be inserted, otherwise zero.
|
||||
*
|
||||
* This routine is used to handle table initialization.
|
||||
*/
|
||||
static int ir_setkeytable(struct input_dev *dev,
|
||||
struct ir_scancode_table *to,
|
||||
static int ir_setkeytable(struct ir_input_dev *ir_dev,
|
||||
const struct ir_scancode_table *from)
|
||||
{
|
||||
struct ir_input_dev *ir_dev = input_get_drvdata(dev);
|
||||
struct ir_scancode_table *rc_tab = &ir_dev->rc_tab;
|
||||
unsigned long flags;
|
||||
unsigned int i;
|
||||
int rc = 0;
|
||||
unsigned int i, index;
|
||||
int rc;
|
||||
|
||||
rc = ir_create_table(&ir_dev->rc_tab,
|
||||
from->name, from->ir_type, from->size);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n",
|
||||
rc_tab->size, rc_tab->alloc);
|
||||
|
||||
spin_lock_irqsave(&rc_tab->lock, flags);
|
||||
for (i = 0; i < from->size; i++) {
|
||||
rc = ir_do_setkeycode(dev, to, from->scan[i].scancode,
|
||||
from->scan[i].keycode, false);
|
||||
if (rc)
|
||||
index = ir_establish_scancode(ir_dev, rc_tab,
|
||||
from->scan[i].scancode, false);
|
||||
if (index >= rc_tab->len) {
|
||||
rc = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
ir_update_mapping(ir_dev->input_dev, rc_tab, index,
|
||||
from->scan[i].keycode);
|
||||
}
|
||||
spin_unlock_irqrestore(&rc_tab->lock, flags);
|
||||
|
||||
if (rc)
|
||||
ir_free_table(rc_tab);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ir_lookup_by_scancode() - locate mapping by scancode
|
||||
* @rc_tab: the &struct ir_scancode_table to search
|
||||
* @scancode: scancode to look for in the table
|
||||
* @return: index in the table, -1U if not found
|
||||
*
|
||||
* This routine performs binary search in RC keykeymap table for
|
||||
* given scancode.
|
||||
*/
|
||||
static unsigned int ir_lookup_by_scancode(const struct ir_scancode_table *rc_tab,
|
||||
unsigned int scancode)
|
||||
{
|
||||
unsigned int start = 0;
|
||||
unsigned int end = rc_tab->len - 1;
|
||||
unsigned int mid;
|
||||
|
||||
while (start <= end) {
|
||||
mid = (start + end) / 2;
|
||||
if (rc_tab->scan[mid].scancode < scancode)
|
||||
start = mid + 1;
|
||||
else if (rc_tab->scan[mid].scancode > scancode)
|
||||
end = mid - 1;
|
||||
else
|
||||
return mid;
|
||||
}
|
||||
|
||||
return -1U;
|
||||
}
|
||||
|
||||
/**
|
||||
* ir_getkeycode() - get a keycode from the scancode->keycode table
|
||||
* @dev: the struct input_dev device descriptor
|
||||
@ -224,36 +352,46 @@ static int ir_setkeytable(struct input_dev *dev,
|
||||
* This routine is used to handle evdev EVIOCGKEY ioctl.
|
||||
*/
|
||||
static int ir_getkeycode(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int *keycode)
|
||||
struct input_keymap_entry *ke)
|
||||
{
|
||||
int start, end, mid;
|
||||
unsigned long flags;
|
||||
int key = KEY_RESERVED;
|
||||
struct ir_input_dev *ir_dev = input_get_drvdata(dev);
|
||||
struct ir_scancode_table *rc_tab = &ir_dev->rc_tab;
|
||||
struct ir_scancode *entry;
|
||||
unsigned long flags;
|
||||
unsigned int index;
|
||||
unsigned int scancode;
|
||||
int retval;
|
||||
|
||||
spin_lock_irqsave(&rc_tab->lock, flags);
|
||||
start = 0;
|
||||
end = rc_tab->len - 1;
|
||||
while (start <= end) {
|
||||
mid = (start + end) / 2;
|
||||
if (rc_tab->scan[mid].scancode < scancode)
|
||||
start = mid + 1;
|
||||
else if (rc_tab->scan[mid].scancode > scancode)
|
||||
end = mid - 1;
|
||||
else {
|
||||
key = rc_tab->scan[mid].keycode;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
|
||||
index = ke->index;
|
||||
} else {
|
||||
retval = input_scancode_to_scalar(ke, &scancode);
|
||||
if (retval)
|
||||
goto out;
|
||||
|
||||
index = ir_lookup_by_scancode(rc_tab, scancode);
|
||||
}
|
||||
|
||||
if (index >= rc_tab->len) {
|
||||
if (!(ke->flags & INPUT_KEYMAP_BY_INDEX))
|
||||
IR_dprintk(1, "unknown key for scancode 0x%04x\n",
|
||||
scancode);
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
entry = &rc_tab->scan[index];
|
||||
|
||||
ke->index = index;
|
||||
ke->keycode = entry->keycode;
|
||||
ke->len = sizeof(entry->scancode);
|
||||
memcpy(ke->scancode, &entry->scancode, sizeof(entry->scancode));
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&rc_tab->lock, flags);
|
||||
|
||||
if (key == KEY_RESERVED)
|
||||
IR_dprintk(1, "unknown key for scancode 0x%04x\n",
|
||||
scancode);
|
||||
|
||||
*keycode = key;
|
||||
return 0;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -268,12 +406,24 @@ static int ir_getkeycode(struct input_dev *dev,
|
||||
*/
|
||||
u32 ir_g_keycode_from_table(struct input_dev *dev, u32 scancode)
|
||||
{
|
||||
int keycode;
|
||||
struct ir_input_dev *ir_dev = input_get_drvdata(dev);
|
||||
struct ir_scancode_table *rc_tab = &ir_dev->rc_tab;
|
||||
unsigned int keycode;
|
||||
unsigned int index;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&rc_tab->lock, flags);
|
||||
|
||||
index = ir_lookup_by_scancode(rc_tab, scancode);
|
||||
keycode = index < rc_tab->len ?
|
||||
rc_tab->scan[index].keycode : KEY_RESERVED;
|
||||
|
||||
spin_unlock_irqrestore(&rc_tab->lock, flags);
|
||||
|
||||
ir_getkeycode(dev, scancode, &keycode);
|
||||
if (keycode != KEY_RESERVED)
|
||||
IR_dprintk(1, "%s: scancode 0x%04x keycode 0x%02x\n",
|
||||
dev->name, scancode, keycode);
|
||||
|
||||
return keycode;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ir_g_keycode_from_table);
|
||||
@ -453,8 +603,8 @@ int __ir_input_register(struct input_dev *input_dev,
|
||||
goto out_dev;
|
||||
}
|
||||
|
||||
input_dev->getkeycode = ir_getkeycode;
|
||||
input_dev->setkeycode = ir_setkeycode;
|
||||
input_dev->getkeycode_new = ir_getkeycode;
|
||||
input_dev->setkeycode_new = ir_setkeycode;
|
||||
input_set_drvdata(input_dev, ir_dev);
|
||||
ir_dev->input_dev = input_dev;
|
||||
|
||||
@ -462,12 +612,6 @@ int __ir_input_register(struct input_dev *input_dev,
|
||||
spin_lock_init(&ir_dev->keylock);
|
||||
setup_timer(&ir_dev->timer_keyup, ir_timer_keyup, (unsigned long)ir_dev);
|
||||
|
||||
ir_dev->rc_tab.name = rc_tab->name;
|
||||
ir_dev->rc_tab.ir_type = rc_tab->ir_type;
|
||||
ir_dev->rc_tab.alloc = roundup_pow_of_two(rc_tab->size *
|
||||
sizeof(struct ir_scancode));
|
||||
ir_dev->rc_tab.scan = kmalloc(ir_dev->rc_tab.alloc, GFP_KERNEL);
|
||||
ir_dev->rc_tab.size = ir_dev->rc_tab.alloc / sizeof(struct ir_scancode);
|
||||
if (props) {
|
||||
ir_dev->props = props;
|
||||
if (props->open)
|
||||
@ -476,23 +620,14 @@ int __ir_input_register(struct input_dev *input_dev,
|
||||
input_dev->close = ir_close;
|
||||
}
|
||||
|
||||
if (!ir_dev->rc_tab.scan) {
|
||||
rc = -ENOMEM;
|
||||
goto out_name;
|
||||
}
|
||||
|
||||
IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n",
|
||||
ir_dev->rc_tab.size, ir_dev->rc_tab.alloc);
|
||||
|
||||
set_bit(EV_KEY, input_dev->evbit);
|
||||
set_bit(EV_REP, input_dev->evbit);
|
||||
set_bit(EV_MSC, input_dev->evbit);
|
||||
set_bit(MSC_SCAN, input_dev->mscbit);
|
||||
|
||||
if (ir_setkeytable(input_dev, &ir_dev->rc_tab, rc_tab)) {
|
||||
rc = -ENOMEM;
|
||||
goto out_table;
|
||||
}
|
||||
rc = ir_setkeytable(ir_dev, rc_tab);
|
||||
if (rc)
|
||||
goto out_name;
|
||||
|
||||
rc = ir_register_class(input_dev);
|
||||
if (rc < 0)
|
||||
@ -515,7 +650,7 @@ int __ir_input_register(struct input_dev *input_dev,
|
||||
out_event:
|
||||
ir_unregister_class(input_dev);
|
||||
out_table:
|
||||
kfree(ir_dev->rc_tab.scan);
|
||||
ir_free_table(&ir_dev->rc_tab);
|
||||
out_name:
|
||||
kfree(ir_dev->driver_name);
|
||||
out_dev:
|
||||
@ -533,7 +668,6 @@ EXPORT_SYMBOL_GPL(__ir_input_register);
|
||||
void ir_input_unregister(struct input_dev *input_dev)
|
||||
{
|
||||
struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
|
||||
struct ir_scancode_table *rc_tab;
|
||||
|
||||
if (!ir_dev)
|
||||
return;
|
||||
@ -545,10 +679,7 @@ void ir_input_unregister(struct input_dev *input_dev)
|
||||
if (ir_dev->props->driver_type == RC_DRIVER_IR_RAW)
|
||||
ir_raw_event_unregister(input_dev);
|
||||
|
||||
rc_tab = &ir_dev->rc_tab;
|
||||
rc_tab->size = 0;
|
||||
kfree(rc_tab->scan);
|
||||
rc_tab->scan = NULL;
|
||||
ir_free_table(&ir_dev->rc_tab);
|
||||
|
||||
ir_unregister_class(input_dev);
|
||||
|
||||
|
@ -338,6 +338,21 @@ static struct resource ab8500_rtc_resources[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource ab8500_poweronkey_db_resources[] = {
|
||||
{
|
||||
.name = "ONKEY_DBF",
|
||||
.start = AB8500_INT_PON_KEY1DB_F,
|
||||
.end = AB8500_INT_PON_KEY1DB_F,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
.name = "ONKEY_DBR",
|
||||
.start = AB8500_INT_PON_KEY1DB_R,
|
||||
.end = AB8500_INT_PON_KEY1DB_R,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell ab8500_devs[] = {
|
||||
{
|
||||
.name = "ab8500-gpadc",
|
||||
@ -354,6 +369,11 @@ static struct mfd_cell ab8500_devs[] = {
|
||||
{ .name = "ab8500-usb", },
|
||||
{ .name = "ab8500-pwm", },
|
||||
{ .name = "ab8500-regulator", },
|
||||
{
|
||||
.name = "ab8500-poweron-key",
|
||||
.num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
|
||||
.resources = ab8500_poweronkey_db_resources,
|
||||
},
|
||||
};
|
||||
|
||||
int __devinit ab8500_init(struct ab8500 *ab8500)
|
||||
|
@ -141,8 +141,6 @@ source "drivers/staging/adis16255/Kconfig"
|
||||
|
||||
source "drivers/staging/xgifb/Kconfig"
|
||||
|
||||
source "drivers/staging/mrst-touchscreen/Kconfig"
|
||||
|
||||
source "drivers/staging/msm/Kconfig"
|
||||
|
||||
source "drivers/staging/lirc/Kconfig"
|
||||
|
@ -52,7 +52,6 @@ obj-$(CONFIG_CXT1E1) += cxt1e1/
|
||||
obj-$(CONFIG_TI_ST) += ti-st/
|
||||
obj-$(CONFIG_ADIS16255) += adis16255/
|
||||
obj-$(CONFIG_FB_XGI) += xgifb/
|
||||
obj-$(CONFIG_TOUCHSCREEN_MRSTOUCH) += mrst-touchscreen/
|
||||
obj-$(CONFIG_MSM_STAGING) += msm/
|
||||
obj-$(CONFIG_EASYCAP) += easycap/
|
||||
obj-$(CONFIG_SOLO6X10) += solo6x10/
|
||||
|
@ -1,7 +0,0 @@
|
||||
config TOUCHSCREEN_INTEL_MID
|
||||
tristate "Intel MID platform resistive touchscreen"
|
||||
depends on INTEL_SCU_IPC
|
||||
default y
|
||||
help
|
||||
Say Y here if you have a Intel MID based touchscreen
|
||||
If unsure, say N.
|
@ -1,3 +0,0 @@
|
||||
obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) := intel_mid_touch.o
|
||||
|
||||
|
@ -1,2 +0,0 @@
|
||||
- Move the driver to not think it is SPI (requires fixing some of the SFI
|
||||
and firmware side)
|
@ -1,864 +0,0 @@
|
||||
/*
|
||||
* intel_mid_touch.c - Intel MID Resistive Touch Screen Driver
|
||||
*
|
||||
* Copyright (C) 2008 Intel Corp
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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; ifnot, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*
|
||||
* Questions/Comments/Bug fixes to Sreedhara (sreedhara.ds@intel.com)
|
||||
* Ramesh Agarwal (ramesh.agarwal@intel.com)
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* TODO:
|
||||
* kill off mrstouch_debug eventually
|
||||
* review conversion of r/m/w sequences
|
||||
* Replace interrupt mutex abuse
|
||||
* Kill of mrstouchdevp pointer
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/param.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
|
||||
#if defined(MRSTOUCH_DEBUG)
|
||||
#define mrstouch_debug(fmt, args...)\
|
||||
do { \
|
||||
printk(KERN_DEBUG "\n[MRSTOUCH(%d)] - ", __LINE__); \
|
||||
printk(KERN_DEBUG fmt, ##args); \
|
||||
} while (0);
|
||||
#else
|
||||
#define mrstouch_debug(fmt, args...)
|
||||
#endif
|
||||
|
||||
/* PMIC Interrupt registers */
|
||||
#define PMIC_REG_ID1 0x00 /*PMIC ID1 register */
|
||||
|
||||
/* PMIC Interrupt registers */
|
||||
#define PMIC_REG_INT 0x04 /*PMIC interrupt register */
|
||||
#define PMIC_REG_MINT 0x05 /*PMIC interrupt mask register */
|
||||
|
||||
/* ADC Interrupt registers */
|
||||
#define PMIC_REG_ADCINT 0x5F /*ADC interrupt register */
|
||||
#define PMIC_REG_MADCINT 0x60 /*ADC interrupt mask register */
|
||||
|
||||
/* ADC Control registers */
|
||||
#define PMIC_REG_ADCCNTL1 0x61 /*ADC control register */
|
||||
|
||||
/* ADC Channel Selection registers */
|
||||
#define PMICADDR0 0xA4
|
||||
#define END_OF_CHANNEL 0x1F
|
||||
|
||||
/* ADC Result register */
|
||||
#define PMIC_REG_ADCSNS0H 0x64
|
||||
|
||||
/* ADC channels for touch screen */
|
||||
#define MRST_TS_CHAN10 0xA /* Touch screen X+ connection */
|
||||
#define MRST_TS_CHAN11 0xB /* Touch screen X- connection */
|
||||
#define MRST_TS_CHAN12 0xC /* Touch screen Y+ connection */
|
||||
#define MRST_TS_CHAN13 0xD /* Touch screen Y- connection */
|
||||
|
||||
/* Touch screen coordinate constants */
|
||||
#define TOUCH_PRESSURE 50
|
||||
#define TOUCH_PRESSURE_FS 100
|
||||
|
||||
#define XMOVE_LIMIT 5
|
||||
#define YMOVE_LIMIT 5
|
||||
#define XYMOVE_CNT 3
|
||||
|
||||
#define MAX_10BIT ((1<<10)-1)
|
||||
|
||||
/* Touch screen channel BIAS constants */
|
||||
#define XBIAS 0x20
|
||||
#define YBIAS 0x40
|
||||
#define ZBIAS 0x80
|
||||
|
||||
/* Touch screen coordinates */
|
||||
#define MIN_X 10
|
||||
#define MAX_X 1024
|
||||
#define MIN_Y 10
|
||||
#define MAX_Y 1024
|
||||
#define WAIT_ADC_COMPLETION 10
|
||||
|
||||
/* PMIC ADC round robin delays */
|
||||
#define ADC_LOOP_DELAY0 0x0 /* Continuous loop */
|
||||
#define ADC_LOOP_DELAY1 0x1 /* 4.5 ms approximate */
|
||||
|
||||
/* PMIC Vendor Identifiers */
|
||||
#define PMIC_VENDOR_FS 0 /* PMIC vendor FreeScale */
|
||||
#define PMIC_VENDOR_MAXIM 1 /* PMIC vendor MAXIM */
|
||||
#define PMIC_VENDOR_NEC 2 /* PMIC vendor NEC */
|
||||
#define MRSTOUCH_MAX_CHANNELS 32 /* Maximum ADC channels */
|
||||
|
||||
/* Touch screen device structure */
|
||||
struct mrstouch_dev {
|
||||
struct spi_device *spi; /* SPI device associated with touch screen */
|
||||
struct input_dev *input; /* input device for touchscreen*/
|
||||
char phys[32]; /* Device name */
|
||||
struct task_struct *pendet_thrd; /* PENDET interrupt handler */
|
||||
struct mutex lock; /* Sync between interrupt and PENDET handler */
|
||||
bool busy; /* Busy flag */
|
||||
u16 asr; /* Address selection register */
|
||||
int irq; /* Touch screen IRQ # */
|
||||
uint vendor; /* PMIC vendor */
|
||||
uint rev; /* PMIC revision */
|
||||
bool suspended; /* Device suspended status */
|
||||
bool disabled; /* Device disabled status */
|
||||
u16 x; /* X coordinate */
|
||||
u16 y; /* Y coordinate */
|
||||
bool pendown; /* PEN position */
|
||||
} ;
|
||||
|
||||
|
||||
/* Global Pointer to Touch screen device */
|
||||
static struct mrstouch_dev *mrstouchdevp;
|
||||
|
||||
/* Utility to read PMIC ID */
|
||||
static int mrstouch_pmic_id(uint *vendor, uint *rev)
|
||||
{
|
||||
int err;
|
||||
u8 r;
|
||||
|
||||
err = intel_scu_ipc_ioread8(PMIC_REG_ID1, &r);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*vendor = r & 0x7;
|
||||
*rev = (r >> 3) & 0x7;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse ADC channels to find end of the channel configured by other ADC user
|
||||
* NEC and MAXIM requires 4 channels and FreeScale needs 18 channels
|
||||
*/
|
||||
static int mrstouch_chan_parse(struct mrstouch_dev *tsdev)
|
||||
{
|
||||
int err, i, j, found;
|
||||
u32 r32;
|
||||
|
||||
found = -1;
|
||||
|
||||
for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) {
|
||||
if (found >= 0)
|
||||
break;
|
||||
|
||||
err = intel_scu_ipc_ioread32(PMICADDR0, &r32);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
for (j = 0; j < 32; j+= 8) {
|
||||
if (((r32 >> j) & 0xFF) == END_OF_CHANNEL) {
|
||||
found = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found < 0)
|
||||
return 0;
|
||||
|
||||
if (tsdev->vendor == PMIC_VENDOR_FS) {
|
||||
if (found && found > (MRSTOUCH_MAX_CHANNELS - 18))
|
||||
return -ENOSPC;
|
||||
} else {
|
||||
if (found && found > (MRSTOUCH_MAX_CHANNELS - 4))
|
||||
return -ENOSPC;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/* Utility to enable/disable pendet.
|
||||
* pendet set to true enables PENDET interrupt
|
||||
* pendet set to false disables PENDET interrupt
|
||||
* Also clears RND mask bit
|
||||
*/
|
||||
static int pendet_enable(struct mrstouch_dev *tsdev, bool pendet)
|
||||
{
|
||||
u16 reg;
|
||||
u8 r;
|
||||
u8 pendet_enabled = 0;
|
||||
int retry = 0;
|
||||
int err;
|
||||
|
||||
err = intel_scu_ipc_ioread16(PMIC_REG_MADCINT, ®);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (pendet) {
|
||||
reg &= ~0x0005;
|
||||
reg |= 0x2000; /* Enable pendet */
|
||||
} else
|
||||
reg &= 0xDFFF; /* Disable pendet */
|
||||
|
||||
/* Set MADCINT and update ADCCNTL1 (next reg byte) */
|
||||
err = intel_scu_ipc_iowrite16(PMIC_REG_MADCINT, reg);
|
||||
if (!pendet || err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Sometimes even after the register write succeeds
|
||||
* the PMIC register value is not updated. Retry few iterations
|
||||
* to enable pendet.
|
||||
*/
|
||||
|
||||
err = intel_scu_ipc_ioread8(PMIC_REG_ADCCNTL1, &r);
|
||||
pendet_enabled = (r >> 5) & 0x01;
|
||||
|
||||
retry = 0;
|
||||
while (!err && !pendet_enabled) {
|
||||
retry++;
|
||||
msleep(10);
|
||||
err = intel_scu_ipc_iowrite8(PMIC_REG_ADCCNTL1, reg >> 8);
|
||||
if (err)
|
||||
break;
|
||||
err = intel_scu_ipc_ioread8(PMIC_REG_ADCCNTL1, &r);
|
||||
if (err == 0)
|
||||
pendet_enabled = (r >> 5) & 0x01;
|
||||
if (retry >= 10) {
|
||||
dev_err(&tsdev->spi->dev, "Touch screen disabled.\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* To read PMIC ADC touch screen result
|
||||
* Reads ADC storage registers for higher 7 and lower 3 bits
|
||||
* converts the two readings to single value and turns off gain bit
|
||||
*/
|
||||
static int mrstouch_ts_chan_read(u16 offset, u16 chan, u16 *vp, u16 *vm)
|
||||
{
|
||||
int err;
|
||||
u16 result;
|
||||
u32 res;
|
||||
|
||||
result = PMIC_REG_ADCSNS0H + offset;
|
||||
|
||||
if (chan == MRST_TS_CHAN12)
|
||||
result += 4;
|
||||
|
||||
err = intel_scu_ipc_ioread32(result, &res);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Mash the bits up */
|
||||
|
||||
*vp = (res & 0xFF) << 3; /* Highest 7 bits */
|
||||
*vp |= (res >> 8) & 0x07; /* Lower 3 bits */
|
||||
*vp &= 0x3FF;
|
||||
|
||||
res >>= 16;
|
||||
|
||||
*vm = (res & 0xFF) << 3; /* Highest 7 bits */
|
||||
*vm |= (res >> 8) & 0x07; /* Lower 3 bits */
|
||||
*vm &= 0x3FF;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* To configure touch screen channels
|
||||
* Writes touch screen channels to ADC address selection registers
|
||||
*/
|
||||
static int mrstouch_ts_chan_set(uint offset)
|
||||
{
|
||||
int count;
|
||||
u16 chan;
|
||||
u16 reg[5];
|
||||
u8 data[5];
|
||||
|
||||
chan = PMICADDR0 + offset;
|
||||
for (count = 0; count <= 3; count++) {
|
||||
reg[count] = chan++;
|
||||
data[count] = MRST_TS_CHAN10 + count;
|
||||
}
|
||||
reg[count] = chan;
|
||||
data[count] = END_OF_CHANNEL;
|
||||
|
||||
return intel_scu_ipc_writev(reg, data, 5);
|
||||
}
|
||||
|
||||
/* Initialize ADC */
|
||||
static int mrstouch_adc_init(struct mrstouch_dev *tsdev)
|
||||
{
|
||||
int err, start;
|
||||
u8 ra, rm;
|
||||
|
||||
err = mrstouch_pmic_id(&tsdev->vendor, &tsdev->rev);
|
||||
if (err) {
|
||||
dev_err(&tsdev->spi->dev, "Unable to read PMIC id\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
start = mrstouch_chan_parse(tsdev);
|
||||
if (start < 0) {
|
||||
dev_err(&tsdev->spi->dev, "Unable to parse channels\n");
|
||||
return start;
|
||||
}
|
||||
|
||||
tsdev->asr = start;
|
||||
|
||||
mrstouch_debug("Channel offset(%d): 0x%X\n", tsdev->asr, tsdev->vendor);
|
||||
|
||||
/* ADC power on, start, enable PENDET and set loop delay
|
||||
* ADC loop delay is set to 4.5 ms approximately
|
||||
* Loop delay more than this results in jitter in adc readings
|
||||
* Setting loop delay to 0 (continous loop) in MAXIM stops PENDET
|
||||
* interrupt generation sometimes.
|
||||
*/
|
||||
|
||||
if (tsdev->vendor == PMIC_VENDOR_FS) {
|
||||
ra = 0xE0 | ADC_LOOP_DELAY0;
|
||||
rm = 0x5;
|
||||
} else {
|
||||
/* NEC and MAXIm not consistent with loop delay 0 */
|
||||
ra = 0xE0 | ADC_LOOP_DELAY1;
|
||||
rm = 0x0;
|
||||
|
||||
/* configure touch screen channels */
|
||||
err = mrstouch_ts_chan_set(tsdev->asr);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, ra, 0xE7);
|
||||
if (err == 0)
|
||||
err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, rm, 0x03);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Reports x,y coordinates to event subsystem */
|
||||
static void mrstouch_report_xy(struct mrstouch_dev *tsdev, u16 x, u16 y, u16 z)
|
||||
{
|
||||
int xdiff, ydiff;
|
||||
|
||||
if (tsdev->pendown && z <= TOUCH_PRESSURE) {
|
||||
/* Pen removed, report button release */
|
||||
mrstouch_debug("BTN REL(%d)", z);
|
||||
input_report_key(tsdev->input, BTN_TOUCH, 0);
|
||||
tsdev->pendown = false;
|
||||
}
|
||||
|
||||
xdiff = abs(x - tsdev->x);
|
||||
ydiff = abs(y - tsdev->y);
|
||||
|
||||
/*
|
||||
if x and y values changes for XYMOVE_CNT readings it is considered
|
||||
as stylus is moving. This is required to differentiate between stylus
|
||||
movement and jitter
|
||||
*/
|
||||
if (x < MIN_X || x > MAX_X || y < MIN_Y || y > MAX_Y) {
|
||||
/* Spurious values, release button if touched and return */
|
||||
if (tsdev->pendown) {
|
||||
mrstouch_debug("BTN REL(%d)", z);
|
||||
input_report_key(tsdev->input, BTN_TOUCH, 0);
|
||||
tsdev->pendown = false;
|
||||
}
|
||||
return;
|
||||
} else if (xdiff >= XMOVE_LIMIT || ydiff >= YMOVE_LIMIT) {
|
||||
tsdev->x = x;
|
||||
tsdev->y = y;
|
||||
|
||||
input_report_abs(tsdev->input, ABS_X, x);
|
||||
input_report_abs(tsdev->input, ABS_Y, y);
|
||||
input_sync(tsdev->input);
|
||||
}
|
||||
|
||||
|
||||
if (!tsdev->pendown && z > TOUCH_PRESSURE) {
|
||||
/* Pen touched, report button touch */
|
||||
mrstouch_debug("BTN TCH(%d, %d, %d)", x, y, z);
|
||||
input_report_key(tsdev->input, BTN_TOUCH, 1);
|
||||
tsdev->pendown = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Utility to start ADC, used by freescale handler */
|
||||
static int pendet_mask(void)
|
||||
{
|
||||
return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x02, 0x02);
|
||||
}
|
||||
|
||||
/* Utility to stop ADC, used by freescale handler */
|
||||
static int pendet_umask(void)
|
||||
{
|
||||
return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x00, 0x02);
|
||||
}
|
||||
|
||||
/* Utility to read ADC, used by freescale handler */
|
||||
static int mrstouch_pmic_fs_adc_read(struct mrstouch_dev *tsdev)
|
||||
{
|
||||
int err;
|
||||
u16 x, y, z, result;
|
||||
u16 reg[4];
|
||||
u8 data[4];
|
||||
|
||||
result = PMIC_REG_ADCSNS0H + tsdev->asr;
|
||||
|
||||
reg[0] = result + 4;
|
||||
reg[1] = result + 5;
|
||||
reg[2] = result + 16;
|
||||
reg[3] = result + 17;
|
||||
|
||||
err = intel_scu_ipc_readv(reg, data, 4);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
x = data[0] << 3; /* Higher 7 bits */
|
||||
x |= data[1] & 0x7; /* Lower 3 bits */
|
||||
x &= 0x3FF;
|
||||
|
||||
y = data[2] << 3; /* Higher 7 bits */
|
||||
y |= data[3] & 0x7; /* Lower 3 bits */
|
||||
y &= 0x3FF;
|
||||
|
||||
/* Read Z value */
|
||||
reg[0] = result + 28;
|
||||
reg[1] = result + 29;
|
||||
|
||||
err = intel_scu_ipc_readv(reg, data, 4);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
z = data[0] << 3; /* Higher 7 bits */
|
||||
z |= data[1] & 0x7; /* Lower 3 bits */
|
||||
z &= 0x3FF;
|
||||
|
||||
#if defined(MRSTOUCH_PRINT_XYZP)
|
||||
mrstouch_debug("X: %d, Y: %d, Z: %d", x, y, z);
|
||||
#endif
|
||||
|
||||
if (z >= TOUCH_PRESSURE_FS) {
|
||||
mrstouch_report_xy(tsdev, x, y, TOUCH_PRESSURE - 1); /* Pen Removed */
|
||||
return TOUCH_PRESSURE - 1;
|
||||
} else {
|
||||
mrstouch_report_xy(tsdev, x, y, TOUCH_PRESSURE + 1); /* Pen Touched */
|
||||
return TOUCH_PRESSURE + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
ipc_error:
|
||||
dev_err(&tsdev->spi->dev, "ipc error during fs_adc read\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* To handle free scale pmic pendet interrupt */
|
||||
static int pmic0_pendet(void *dev_id)
|
||||
{
|
||||
int err, count;
|
||||
u16 chan;
|
||||
unsigned int touched;
|
||||
struct mrstouch_dev *tsdev = (struct mrstouch_dev *)dev_id;
|
||||
u16 reg[5];
|
||||
u8 data[5];
|
||||
|
||||
chan = PMICADDR0 + tsdev->asr;
|
||||
|
||||
/* Set X BIAS */
|
||||
for (count = 0; count <= 3; count++) {
|
||||
reg[count] = chan++;
|
||||
data[count] = 0x2A;
|
||||
}
|
||||
reg[count] = chan++; /* Dummy */
|
||||
data[count] = 0;
|
||||
|
||||
err = intel_scu_ipc_writev(reg, data, 5);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
msleep(WAIT_ADC_COMPLETION);
|
||||
|
||||
/* Set Y BIAS */
|
||||
for (count = 0; count <= 3; count++) {
|
||||
reg[count] = chan++;
|
||||
data[count] = 0x4A;
|
||||
}
|
||||
reg[count] = chan++; /* Dummy */
|
||||
data[count] = 0;
|
||||
|
||||
err = intel_scu_ipc_writev(reg, data, 5);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
msleep(WAIT_ADC_COMPLETION);
|
||||
|
||||
/* Set Z BIAS */
|
||||
err = intel_scu_ipc_iowrite32(chan + 2, 0x8A8A8A8A);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
msleep(WAIT_ADC_COMPLETION);
|
||||
|
||||
/*Read touch screen channels till pen removed
|
||||
* Freescale reports constant value of z for all points
|
||||
* z is high when screen is not touched and low when touched
|
||||
* Map high z value to not touched and low z value to pen touched
|
||||
*/
|
||||
touched = mrstouch_pmic_fs_adc_read(tsdev);
|
||||
while (touched > TOUCH_PRESSURE) {
|
||||
touched = mrstouch_pmic_fs_adc_read(tsdev);
|
||||
msleep(WAIT_ADC_COMPLETION);
|
||||
}
|
||||
|
||||
/* Clear all TS channels */
|
||||
chan = PMICADDR0 + tsdev->asr;
|
||||
for (count = 0; count <= 4; count++) {
|
||||
reg[count] = chan++;
|
||||
data[count] = 0;
|
||||
}
|
||||
err = intel_scu_ipc_writev(reg, data, 5);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
for (count = 0; count <= 4; count++) {
|
||||
reg[count] = chan++;
|
||||
data[count] = 0;
|
||||
}
|
||||
err = intel_scu_ipc_writev(reg, data, 5);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
err = intel_scu_ipc_iowrite32(chan + 2, 0x00000000);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
return 0;
|
||||
|
||||
ipc_error:
|
||||
dev_err(&tsdev->spi->dev, "ipc error during pendet\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* To enable X, Y and Z bias values
|
||||
* Enables YPYM for X channels and XPXM for Y channels
|
||||
*/
|
||||
static int mrstouch_ts_bias_set(uint offset, uint bias)
|
||||
{
|
||||
int count;
|
||||
u16 chan, start;
|
||||
u16 reg[4];
|
||||
u8 data[4];
|
||||
|
||||
chan = PMICADDR0 + offset;
|
||||
start = MRST_TS_CHAN10;
|
||||
|
||||
for (count = 0; count <= 3; count++) {
|
||||
reg[count] = chan++;
|
||||
data[count] = bias | (start + count);
|
||||
}
|
||||
return intel_scu_ipc_writev(reg, data, 4);
|
||||
}
|
||||
|
||||
/* To read touch screen channel values */
|
||||
static int mrstouch_adc_read(struct mrstouch_dev *tsdev)
|
||||
{
|
||||
int err;
|
||||
u16 xp, xm, yp, ym, zp, zm;
|
||||
|
||||
/* configure Y bias for X channels */
|
||||
err = mrstouch_ts_bias_set(tsdev->asr, YBIAS);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
msleep(WAIT_ADC_COMPLETION);
|
||||
|
||||
/* read x+ and x- channels */
|
||||
err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, &xp, &xm);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
/* configure x bias for y channels */
|
||||
err = mrstouch_ts_bias_set(tsdev->asr, XBIAS);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
msleep(WAIT_ADC_COMPLETION);
|
||||
|
||||
/* read y+ and y- channels */
|
||||
err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN12, &yp, &ym);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
/* configure z bias for x and y channels */
|
||||
err = mrstouch_ts_bias_set(tsdev->asr, ZBIAS);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
msleep(WAIT_ADC_COMPLETION);
|
||||
|
||||
/* read z+ and z- channels */
|
||||
err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, &zp, &zm);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
#if defined(MRSTOUCH_PRINT_XYZP)
|
||||
printk(KERN_INFO "X+: %d, Y+: %d, Z+: %d\n", xp, yp, zp);
|
||||
#endif
|
||||
|
||||
#if defined(MRSTOUCH_PRINT_XYZM)
|
||||
printk(KERN_INFO "X-: %d, Y-: %d, Z-: %d\n", xm, ym, zm);
|
||||
#endif
|
||||
|
||||
mrstouch_report_xy(tsdev, xp, yp, zp); /* report x and y to eventX */
|
||||
|
||||
return zp;
|
||||
|
||||
ipc_error:
|
||||
dev_err(&tsdev->spi->dev, "ipc error during adc read\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* PENDET interrupt handler function for NEC and MAXIM */
|
||||
static void pmic12_pendet(void *data)
|
||||
{
|
||||
unsigned int touched;
|
||||
struct mrstouch_dev *tsdev = (struct mrstouch_dev *)data;
|
||||
|
||||
/* read touch screen channels till pen removed */
|
||||
do {
|
||||
touched = mrstouch_adc_read(tsdev);
|
||||
} while (touched > TOUCH_PRESSURE);
|
||||
}
|
||||
|
||||
/* Handler to process PENDET interrupt */
|
||||
int mrstouch_pendet(void *data)
|
||||
{
|
||||
struct mrstouch_dev *tsdev = (struct mrstouch_dev *)data;
|
||||
while (1) {
|
||||
/* Wait for PENDET interrupt */
|
||||
if (mutex_lock_interruptible(&tsdev->lock)) {
|
||||
msleep(WAIT_ADC_COMPLETION);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tsdev->busy)
|
||||
return 0;
|
||||
|
||||
tsdev->busy = true;
|
||||
|
||||
if (tsdev->vendor == PMIC_VENDOR_NEC ||
|
||||
tsdev->vendor == PMIC_VENDOR_MAXIM) {
|
||||
/* PENDET must be disabled in NEC before reading ADC */
|
||||
pendet_enable(tsdev,false); /* Disbale PENDET */
|
||||
pmic12_pendet(tsdev);
|
||||
pendet_enable(tsdev, true); /*Enable PENDET */
|
||||
} else if (tsdev->vendor == PMIC_VENDOR_FS) {
|
||||
pendet_umask(); /* Stop ADC */
|
||||
pmic0_pendet(tsdev);
|
||||
pendet_mask(); /* Stop ADC */
|
||||
} else
|
||||
dev_err(&tsdev->spi->dev, "Unsupported touchscreen: %d\n",
|
||||
tsdev->vendor);
|
||||
|
||||
tsdev->busy = false;
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* PENDET interrupt handler */
|
||||
static irqreturn_t pendet_intr_handler(int irq, void *handle)
|
||||
{
|
||||
struct mrstouch_dev *tsdev = (struct mrstouch_dev *)handle;
|
||||
|
||||
mutex_unlock(&tsdev->lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Intializes input device and registers with input subsystem */
|
||||
static int ts_input_dev_init(struct mrstouch_dev *tsdev, struct spi_device *spi)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
mrstouch_debug("%s", __func__);
|
||||
|
||||
tsdev->input = input_allocate_device();
|
||||
if (!tsdev->input) {
|
||||
dev_err(&tsdev->spi->dev, "Unable to allocate input device.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tsdev->input->name = "mrst_touchscreen";
|
||||
snprintf(tsdev->phys, sizeof(tsdev->phys),
|
||||
"%s/input0", dev_name(&spi->dev));
|
||||
tsdev->input->phys = tsdev->phys;
|
||||
tsdev->input->dev.parent = &spi->dev;
|
||||
|
||||
tsdev->input->id.vendor = tsdev->vendor;
|
||||
tsdev->input->id.version = tsdev->rev;
|
||||
|
||||
tsdev->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
tsdev->input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||
|
||||
input_set_abs_params(tsdev->input, ABS_X, MIN_X, MIN_Y, 0, 0);
|
||||
input_set_abs_params(tsdev->input, ABS_Y, MIN_X, MIN_Y, 0, 0);
|
||||
|
||||
err = input_register_device(tsdev->input);
|
||||
if (err) {
|
||||
dev_err(&tsdev->spi->dev, "unable to register input device\n");
|
||||
input_free_device(tsdev->input);
|
||||
return err;
|
||||
}
|
||||
|
||||
mrstouch_debug("%s", "mrstouch initialized");
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* Probe function for touch screen driver */
|
||||
static int __devinit mrstouch_probe(struct spi_device *mrstouch_spi)
|
||||
{
|
||||
int err;
|
||||
unsigned int myirq;
|
||||
struct mrstouch_dev *tsdev;
|
||||
|
||||
mrstouch_debug("%s(%p)", __func__, mrstouch_spi);
|
||||
|
||||
mrstouchdevp = NULL;
|
||||
myirq = mrstouch_spi->irq;
|
||||
|
||||
if (!mrstouch_spi->irq) {
|
||||
dev_err(&mrstouch_spi->dev, "no interrupt assigned\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tsdev = kzalloc(sizeof(struct mrstouch_dev), GFP_KERNEL);
|
||||
if (!tsdev) {
|
||||
dev_err(&mrstouch_spi->dev, "unable to allocate memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tsdev->irq = myirq;
|
||||
mrstouchdevp = tsdev;
|
||||
|
||||
err = mrstouch_adc_init(tsdev);
|
||||
if (err) {
|
||||
dev_err(&mrstouch_spi->dev, "ADC init failed\n");
|
||||
goto mrstouch_err_free_mem;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&mrstouch_spi->dev, tsdev);
|
||||
tsdev->spi = mrstouch_spi;
|
||||
|
||||
err = ts_input_dev_init(tsdev, mrstouch_spi);
|
||||
if (err) {
|
||||
dev_err(&tsdev->spi->dev, "ts_input_dev_init failed");
|
||||
goto mrstouch_err_free_mem;
|
||||
}
|
||||
|
||||
mutex_init(&tsdev->lock);
|
||||
mutex_lock(&tsdev->lock)
|
||||
|
||||
mrstouch_debug("Requesting IRQ-%d", myirq);
|
||||
err = request_irq(myirq, pendet_intr_handler,
|
||||
0, "mrstouch", tsdev);
|
||||
if (err) {
|
||||
dev_err(&tsdev->spi->dev, "unable to allocate irq\n");
|
||||
goto mrstouch_err_free_mem;
|
||||
}
|
||||
|
||||
tsdev->pendet_thrd = kthread_run(mrstouch_pendet,
|
||||
(void *)tsdev, "pendet handler");
|
||||
if (IS_ERR(tsdev->pendet_thrd)) {
|
||||
dev_err(&tsdev->spi->dev, "kthread_run failed\n");
|
||||
err = PTR_ERR(tsdev->pendet_thrd);
|
||||
goto mrstouch_err_free_mem;
|
||||
}
|
||||
mrstouch_debug("%s", "Driver initialized");
|
||||
return 0;
|
||||
|
||||
mrstouch_err_free_mem:
|
||||
kfree(tsdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mrstouch_suspend(struct spi_device *spi, pm_message_t msg)
|
||||
{
|
||||
mrstouch_debug("%s", __func__);
|
||||
mrstouchdevp->suspended = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mrstouch_resume(struct spi_device *spi)
|
||||
{
|
||||
mrstouch_debug("%s", __func__);
|
||||
mrstouchdevp->suspended = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mrstouch_remove(struct spi_device *spi)
|
||||
{
|
||||
mrstouch_debug("%s", __func__);
|
||||
free_irq(mrstouchdevp->irq, mrstouchdevp);
|
||||
input_unregister_device(mrstouchdevp->input);
|
||||
input_free_device(mrstouchdevp->input);
|
||||
if (mrstouchdevp->pendet_thrd)
|
||||
kthread_stop(mrstouchdevp->pendet_thrd);
|
||||
kfree(mrstouchdevp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver mrstouch_driver = {
|
||||
.driver = {
|
||||
.name = "pmic_touch",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = mrstouch_probe,
|
||||
.suspend = mrstouch_suspend,
|
||||
.resume = mrstouch_resume,
|
||||
.remove = mrstouch_remove,
|
||||
};
|
||||
|
||||
static int __init mrstouch_module_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
mrstouch_debug("%s", __func__);
|
||||
err = spi_register_driver(&mrstouch_driver);
|
||||
if (err) {
|
||||
mrstouch_debug("%s(%d)", "SPI PENDET failed", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit mrstouch_module_exit(void)
|
||||
{
|
||||
mrstouch_debug("%s", __func__);
|
||||
spi_unregister_driver(&mrstouch_driver);
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(mrstouch_module_init);
|
||||
module_exit(mrstouch_module_exit);
|
||||
|
||||
MODULE_AUTHOR("Sreedhara Murthy. D.S, sreedhara.ds@intel.com");
|
||||
MODULE_DESCRIPTION("Intel Moorestown Resistive Touch Screen Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -53,9 +53,7 @@ struct gameport {
|
||||
#define to_gameport_port(d) container_of(d, struct gameport, dev)
|
||||
|
||||
struct gameport_driver {
|
||||
|
||||
void *private;
|
||||
char *description;
|
||||
const char *description;
|
||||
|
||||
int (*connect)(struct gameport *, struct gameport_driver *drv);
|
||||
int (*reconnect)(struct gameport *);
|
||||
|
@ -34,7 +34,7 @@ struct input_event {
|
||||
* Protocol version.
|
||||
*/
|
||||
|
||||
#define EV_VERSION 0x010000
|
||||
#define EV_VERSION 0x010001
|
||||
|
||||
/*
|
||||
* IOCTLs (0x00 - 0x7f)
|
||||
@ -56,25 +56,50 @@ struct input_absinfo {
|
||||
__s32 resolution;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct input_keymap_entry - used by EVIOCGKEYCODE/EVIOCSKEYCODE ioctls
|
||||
* @scancode: scancode represented in machine-endian form.
|
||||
* @len: length of the scancode that resides in @scancode buffer.
|
||||
* @index: index in the keymap, may be used instead of scancode
|
||||
* @flags: allows to specify how kernel should handle the request. For
|
||||
* example, setting INPUT_KEYMAP_BY_INDEX flag indicates that kernel
|
||||
* should perform lookup in keymap by @index instead of @scancode
|
||||
* @keycode: key code assigned to this scancode
|
||||
*
|
||||
* The structure is used to retrieve and modify keymap data. Users have
|
||||
* option of performing lookup either by @scancode itself or by @index
|
||||
* in keymap entry. EVIOCGKEYCODE will also return scancode or index
|
||||
* (depending on which element was used to perform lookup).
|
||||
*/
|
||||
struct input_keymap_entry {
|
||||
#define INPUT_KEYMAP_BY_INDEX (1 << 0)
|
||||
__u8 flags;
|
||||
__u8 len;
|
||||
__u16 index;
|
||||
__u32 keycode;
|
||||
__u8 scancode[32];
|
||||
};
|
||||
|
||||
#define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */
|
||||
#define EVIOCGID _IOR('E', 0x02, struct input_id) /* get device ID */
|
||||
#define EVIOCGREP _IOR('E', 0x03, unsigned int[2]) /* get repeat settings */
|
||||
#define EVIOCSREP _IOW('E', 0x03, unsigned int[2]) /* set repeat settings */
|
||||
#define EVIOCGKEYCODE _IOR('E', 0x04, unsigned int[2]) /* get keycode */
|
||||
#define EVIOCSKEYCODE _IOW('E', 0x04, unsigned int[2]) /* set keycode */
|
||||
|
||||
#define EVIOCGKEYCODE _IOR('E', 0x04, struct input_keymap_entry) /* get keycode */
|
||||
#define EVIOCSKEYCODE _IOW('E', 0x04, struct input_keymap_entry) /* set keycode */
|
||||
|
||||
#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */
|
||||
#define EVIOCGPHYS(len) _IOC(_IOC_READ, 'E', 0x07, len) /* get physical location */
|
||||
#define EVIOCGUNIQ(len) _IOC(_IOC_READ, 'E', 0x08, len) /* get unique identifier */
|
||||
|
||||
#define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len) /* get global keystate */
|
||||
#define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len) /* get global key state */
|
||||
#define EVIOCGLED(len) _IOC(_IOC_READ, 'E', 0x19, len) /* get all LEDs */
|
||||
#define EVIOCGSND(len) _IOC(_IOC_READ, 'E', 0x1a, len) /* get all sounds status */
|
||||
#define EVIOCGSW(len) _IOC(_IOC_READ, 'E', 0x1b, len) /* get all switch states */
|
||||
|
||||
#define EVIOCGBIT(ev,len) _IOC(_IOC_READ, 'E', 0x20 + ev, len) /* get event bits */
|
||||
#define EVIOCGABS(abs) _IOR('E', 0x40 + abs, struct input_absinfo) /* get abs value/limits */
|
||||
#define EVIOCSABS(abs) _IOW('E', 0xc0 + abs, struct input_absinfo) /* set abs value/limits */
|
||||
#define EVIOCGABS(abs) _IOR('E', 0x40 + abs, struct input_absinfo) /* get abs value/limits */
|
||||
#define EVIOCSABS(abs) _IOW('E', 0xc0 + abs, struct input_absinfo) /* set abs value/limits */
|
||||
|
||||
#define EVIOCSFF _IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect)) /* send a force effect to a force feedback device */
|
||||
#define EVIOCRMFF _IOW('E', 0x81, int) /* Erase a force effect */
|
||||
@ -1088,13 +1113,13 @@ struct input_mt_slot {
|
||||
* @keycodemax: size of keycode table
|
||||
* @keycodesize: size of elements in keycode table
|
||||
* @keycode: map of scancodes to keycodes for this device
|
||||
* @getkeycode: optional legacy method to retrieve current keymap.
|
||||
* @setkeycode: optional method to alter current keymap, used to implement
|
||||
* sparse keymaps. If not supplied default mechanism will be used.
|
||||
* The method is being called while holding event_lock and thus must
|
||||
* not sleep
|
||||
* @getkeycode: optional method to retrieve current keymap. If not supplied
|
||||
* default mechanism will be used. The method is being called while
|
||||
* holding event_lock and thus must not sleep
|
||||
* @getkeycode_new: transition method
|
||||
* @setkeycode_new: transition method
|
||||
* @ff: force feedback structure associated with the device if device
|
||||
* supports force feedback effects
|
||||
* @repeat_key: stores key code of the last key pressed; used to implement
|
||||
@ -1168,10 +1193,16 @@ struct input_dev {
|
||||
unsigned int keycodemax;
|
||||
unsigned int keycodesize;
|
||||
void *keycode;
|
||||
|
||||
int (*setkeycode)(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int keycode);
|
||||
int (*getkeycode)(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int *keycode);
|
||||
int (*setkeycode_new)(struct input_dev *dev,
|
||||
const struct input_keymap_entry *ke,
|
||||
unsigned int *old_keycode);
|
||||
int (*getkeycode_new)(struct input_dev *dev,
|
||||
struct input_keymap_entry *ke);
|
||||
|
||||
struct ff_device *ff;
|
||||
|
||||
@ -1478,10 +1509,12 @@ INPUT_GENERATE_ABS_ACCESSORS(fuzz, fuzz)
|
||||
INPUT_GENERATE_ABS_ACCESSORS(flat, flat)
|
||||
INPUT_GENERATE_ABS_ACCESSORS(res, resolution)
|
||||
|
||||
int input_get_keycode(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int *keycode);
|
||||
int input_scancode_to_scalar(const struct input_keymap_entry *ke,
|
||||
unsigned int *scancode);
|
||||
|
||||
int input_get_keycode(struct input_dev *dev, struct input_keymap_entry *ke);
|
||||
int input_set_keycode(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int keycode);
|
||||
const struct input_keymap_entry *ke);
|
||||
|
||||
extern struct class input_class;
|
||||
|
||||
|
44
include/linux/input/bu21013.h
Normal file
44
include/linux/input/bu21013.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
* Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
|
||||
* License terms:GNU General Public License (GPL) version 2
|
||||
*/
|
||||
|
||||
#ifndef _BU21013_H
|
||||
#define _BU21013_H
|
||||
|
||||
/**
|
||||
* struct bu21013_platform_device - Handle the platform data
|
||||
* @cs_en: pointer to the cs enable function
|
||||
* @cs_dis: pointer to the cs disable function
|
||||
* @irq_read_val: pointer to read the pen irq value function
|
||||
* @x_max_res: xmax resolution
|
||||
* @y_max_res: ymax resolution
|
||||
* @touch_x_max: touch x max
|
||||
* @touch_y_max: touch y max
|
||||
* @cs_pin: chip select pin
|
||||
* @irq: irq pin
|
||||
* @ext_clk: external clock flag
|
||||
* @x_flip: x flip flag
|
||||
* @y_flip: y flip flag
|
||||
* @wakeup: wakeup flag
|
||||
*
|
||||
* This is used to handle the platform data
|
||||
*/
|
||||
struct bu21013_platform_device {
|
||||
int (*cs_en)(int reset_pin);
|
||||
int (*cs_dis)(int reset_pin);
|
||||
int (*irq_read_val)(void);
|
||||
int x_max_res;
|
||||
int y_max_res;
|
||||
int touch_x_max;
|
||||
int touch_y_max;
|
||||
unsigned int cs_pin;
|
||||
unsigned int irq;
|
||||
bool ext_clk;
|
||||
bool x_flip;
|
||||
bool y_flip;
|
||||
bool wakeup;
|
||||
};
|
||||
|
||||
#endif
|
@ -41,7 +41,9 @@ struct serio {
|
||||
int (*start)(struct serio *);
|
||||
void (*stop)(struct serio *);
|
||||
|
||||
struct serio *parent, *child;
|
||||
struct serio *parent;
|
||||
struct list_head child_node; /* Entry in parent->children list */
|
||||
struct list_head children;
|
||||
unsigned int depth; /* level of nesting in serio hierarchy */
|
||||
|
||||
struct serio_driver *drv; /* accessed from interrupt, must be protected by serio->lock and serio->sem */
|
||||
@ -54,10 +56,9 @@ struct serio {
|
||||
#define to_serio_port(d) container_of(d, struct serio, dev)
|
||||
|
||||
struct serio_driver {
|
||||
void *private;
|
||||
char *description;
|
||||
const char *description;
|
||||
|
||||
struct serio_device_id *id_table;
|
||||
const struct serio_device_id *id_table;
|
||||
bool manual_bind;
|
||||
|
||||
void (*write_wakeup)(struct serio *);
|
||||
@ -197,5 +198,6 @@ static inline void serio_continue_rx(struct serio *serio)
|
||||
#define SERIO_W8001 0x39
|
||||
#define SERIO_DYNAPRO 0x3a
|
||||
#define SERIO_HAMPSHIRE 0x3b
|
||||
#define SERIO_PS2MULT 0x3c
|
||||
|
||||
#endif
|
||||
|
@ -35,7 +35,7 @@ struct ir_scancode_table {
|
||||
unsigned int len; /* Used number of entries */
|
||||
unsigned int alloc; /* Size of *scan in bytes */
|
||||
u64 ir_type;
|
||||
char *name;
|
||||
const char *name;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user