Merge branch 'next' into for-linus
Prepare input updates for 4.18 merge window.
This commit is contained in:
@@ -1943,8 +1943,7 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("input_set_capability: unknown type %u (code %u)\n",
|
||||
type, code);
|
||||
pr_err("%s: unknown type %u (code %u)\n", __func__, type, code);
|
||||
dump_stack();
|
||||
return;
|
||||
}
|
||||
|
@@ -163,7 +163,7 @@ static unsigned int get_time_pit(void)
|
||||
#define GET_TIME(x) do { x = (unsigned int)rdtsc(); } while (0)
|
||||
#define DELTA(x,y) ((y)-(x))
|
||||
#define TIME_NAME "TSC"
|
||||
#elif defined(__alpha__) || defined(CONFIG_MN10300) || defined(CONFIG_ARM) || defined(CONFIG_ARM64) || defined(CONFIG_PPC) || defined(CONFIG_RISCV) || defined(CONFIG_TILE)
|
||||
#elif defined(__alpha__) || defined(CONFIG_ARM) || defined(CONFIG_ARM64) || defined(CONFIG_PPC) || defined(CONFIG_RISCV)
|
||||
#define GET_TIME(x) do { x = get_cycles(); } while (0)
|
||||
#define DELTA(x,y) ((y)-(x))
|
||||
#define TIME_NAME "get_cycles"
|
||||
|
@@ -269,9 +269,7 @@ static int as5011_probe(struct i2c_client *client,
|
||||
input_dev->id.bustype = BUS_I2C;
|
||||
input_dev->dev.parent = &client->dev;
|
||||
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
__set_bit(BTN_JOYSTICK, input_dev->keybit);
|
||||
input_set_capability(input_dev, EV_KEY, BTN_JOYSTICK);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X,
|
||||
AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT);
|
||||
|
@@ -862,7 +862,7 @@ static int gc_setup_pad(struct gc *gc, int idx, int pad_type)
|
||||
|
||||
case GC_N64:
|
||||
for (i = 0; i < 10; i++)
|
||||
__set_bit(gc_n64_btn[i], input_dev->keybit);
|
||||
input_set_capability(input_dev, EV_KEY, gc_n64_btn[i]);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
input_set_abs_params(input_dev, ABS_X + i, -127, 126, 0, 2);
|
||||
@@ -879,26 +879,27 @@ static int gc_setup_pad(struct gc *gc, int idx, int pad_type)
|
||||
break;
|
||||
|
||||
case GC_SNESMOUSE:
|
||||
__set_bit(BTN_LEFT, input_dev->keybit);
|
||||
__set_bit(BTN_RIGHT, input_dev->keybit);
|
||||
__set_bit(REL_X, input_dev->relbit);
|
||||
__set_bit(REL_Y, input_dev->relbit);
|
||||
input_set_capability(input_dev, EV_KEY, BTN_LEFT);
|
||||
input_set_capability(input_dev, EV_KEY, BTN_RIGHT);
|
||||
input_set_capability(input_dev, EV_REL, REL_X);
|
||||
input_set_capability(input_dev, EV_REL, REL_Y);
|
||||
break;
|
||||
|
||||
case GC_SNES:
|
||||
for (i = 4; i < 8; i++)
|
||||
__set_bit(gc_snes_btn[i], input_dev->keybit);
|
||||
input_set_capability(input_dev, EV_KEY, gc_snes_btn[i]);
|
||||
/* fall through */
|
||||
case GC_NES:
|
||||
for (i = 0; i < 4; i++)
|
||||
__set_bit(gc_snes_btn[i], input_dev->keybit);
|
||||
input_set_capability(input_dev, EV_KEY, gc_snes_btn[i]);
|
||||
break;
|
||||
|
||||
case GC_MULTI2:
|
||||
__set_bit(BTN_THUMB, input_dev->keybit);
|
||||
input_set_capability(input_dev, EV_KEY, BTN_THUMB);
|
||||
/* fall through */
|
||||
case GC_MULTI:
|
||||
__set_bit(BTN_TRIGGER, input_dev->keybit);
|
||||
input_set_capability(input_dev, EV_KEY, BTN_TRIGGER);
|
||||
/* fall through */
|
||||
break;
|
||||
|
||||
case GC_PSX:
|
||||
@@ -906,15 +907,17 @@ static int gc_setup_pad(struct gc *gc, int idx, int pad_type)
|
||||
input_set_abs_params(input_dev,
|
||||
gc_psx_abs[i], 4, 252, 0, 2);
|
||||
for (i = 0; i < 12; i++)
|
||||
__set_bit(gc_psx_btn[i], input_dev->keybit);
|
||||
input_set_capability(input_dev, EV_KEY, gc_psx_btn[i]);
|
||||
break;
|
||||
|
||||
break;
|
||||
|
||||
case GC_DDR:
|
||||
for (i = 0; i < 4; i++)
|
||||
__set_bit(gc_psx_ddr_btn[i], input_dev->keybit);
|
||||
input_set_capability(input_dev, EV_KEY,
|
||||
gc_psx_ddr_btn[i]);
|
||||
for (i = 0; i < 12; i++)
|
||||
__set_bit(gc_psx_btn[i], input_dev->keybit);
|
||||
input_set_capability(input_dev, EV_KEY, gc_psx_btn[i]);
|
||||
|
||||
break;
|
||||
}
|
||||
|
@@ -86,8 +86,10 @@
|
||||
|
||||
#define XPAD_PKT_LEN 64
|
||||
|
||||
/* xbox d-pads should map to buttons, as is required for DDR pads
|
||||
but we map them to axes when possible to simplify things */
|
||||
/*
|
||||
* xbox d-pads should map to buttons, as is required for DDR pads
|
||||
* but we map them to axes when possible to simplify things
|
||||
*/
|
||||
#define MAP_DPAD_TO_BUTTONS (1 << 0)
|
||||
#define MAP_TRIGGERS_TO_BUTTONS (1 << 1)
|
||||
#define MAP_STICKS_TO_NULL (1 << 2)
|
||||
@@ -123,6 +125,7 @@ static const struct xpad_device {
|
||||
u8 mapping;
|
||||
u8 xtype;
|
||||
} xpad_device[] = {
|
||||
{ 0x0079, 0x18d4, "GPD Win 2 Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x044f, 0x0f00, "Thrustmaster Wheel", 0, XTYPE_XBOX },
|
||||
{ 0x044f, 0x0f03, "Thrustmaster Wheel", 0, XTYPE_XBOX },
|
||||
{ 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX },
|
||||
@@ -387,15 +390,15 @@ static const signed short xpad_abs_triggers[] = {
|
||||
* match against vendor id as well. Wired Xbox 360 devices have protocol 1,
|
||||
* wireless controllers have protocol 129.
|
||||
*/
|
||||
#define XPAD_XBOX360_VENDOR_PROTOCOL(vend,pr) \
|
||||
#define XPAD_XBOX360_VENDOR_PROTOCOL(vend, pr) \
|
||||
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \
|
||||
.idVendor = (vend), \
|
||||
.bInterfaceClass = USB_CLASS_VENDOR_SPEC, \
|
||||
.bInterfaceSubClass = 93, \
|
||||
.bInterfaceProtocol = (pr)
|
||||
#define XPAD_XBOX360_VENDOR(vend) \
|
||||
{ XPAD_XBOX360_VENDOR_PROTOCOL(vend,1) }, \
|
||||
{ XPAD_XBOX360_VENDOR_PROTOCOL(vend,129) }
|
||||
{ XPAD_XBOX360_VENDOR_PROTOCOL((vend), 1) }, \
|
||||
{ XPAD_XBOX360_VENDOR_PROTOCOL((vend), 129) }
|
||||
|
||||
/* The Xbox One controller uses subclass 71 and protocol 208. */
|
||||
#define XPAD_XBOXONE_VENDOR_PROTOCOL(vend, pr) \
|
||||
@@ -405,10 +408,11 @@ static const signed short xpad_abs_triggers[] = {
|
||||
.bInterfaceSubClass = 71, \
|
||||
.bInterfaceProtocol = (pr)
|
||||
#define XPAD_XBOXONE_VENDOR(vend) \
|
||||
{ XPAD_XBOXONE_VENDOR_PROTOCOL(vend, 208) }
|
||||
{ XPAD_XBOXONE_VENDOR_PROTOCOL((vend), 208) }
|
||||
|
||||
static const struct usb_device_id xpad_table[] = {
|
||||
{ USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */
|
||||
XPAD_XBOX360_VENDOR(0x0079), /* GPD Win 2 Controller */
|
||||
XPAD_XBOX360_VENDOR(0x044f), /* Thrustmaster X-Box 360 controllers */
|
||||
XPAD_XBOX360_VENDOR(0x045e), /* Microsoft X-Box 360 controllers */
|
||||
XPAD_XBOXONE_VENDOR(0x045e), /* Microsoft X-Box One controllers */
|
||||
@@ -1573,7 +1577,6 @@ static void xpad_close(struct input_dev *dev)
|
||||
static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
|
||||
{
|
||||
struct usb_xpad *xpad = input_get_drvdata(input_dev);
|
||||
set_bit(abs, input_dev->absbit);
|
||||
|
||||
switch (abs) {
|
||||
case ABS_X:
|
||||
@@ -1593,6 +1596,9 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
|
||||
case ABS_HAT0Y: /* the d-pad (only if dpad is mapped to axes */
|
||||
input_set_abs_params(input_dev, abs, -1, 1, 0, 0);
|
||||
break;
|
||||
default:
|
||||
input_set_abs_params(input_dev, abs, 0, 0, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1633,10 +1639,7 @@ static int xpad_init_input(struct usb_xpad *xpad)
|
||||
input_dev->close = xpad_close;
|
||||
}
|
||||
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
|
||||
if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
/* set up axes */
|
||||
for (i = 0; xpad_abs[i] >= 0; i++)
|
||||
xpad_set_up_abs(input_dev, xpad_abs[i]);
|
||||
@@ -1644,21 +1647,22 @@ static int xpad_init_input(struct usb_xpad *xpad)
|
||||
|
||||
/* set up standard buttons */
|
||||
for (i = 0; xpad_common_btn[i] >= 0; i++)
|
||||
__set_bit(xpad_common_btn[i], input_dev->keybit);
|
||||
input_set_capability(input_dev, EV_KEY, xpad_common_btn[i]);
|
||||
|
||||
/* set up model-specific ones */
|
||||
if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W ||
|
||||
xpad->xtype == XTYPE_XBOXONE) {
|
||||
for (i = 0; xpad360_btn[i] >= 0; i++)
|
||||
__set_bit(xpad360_btn[i], input_dev->keybit);
|
||||
input_set_capability(input_dev, EV_KEY, xpad360_btn[i]);
|
||||
} else {
|
||||
for (i = 0; xpad_btn[i] >= 0; i++)
|
||||
__set_bit(xpad_btn[i], input_dev->keybit);
|
||||
input_set_capability(input_dev, EV_KEY, xpad_btn[i]);
|
||||
}
|
||||
|
||||
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
|
||||
for (i = 0; xpad_btn_pad[i] >= 0; i++)
|
||||
__set_bit(xpad_btn_pad[i], input_dev->keybit);
|
||||
input_set_capability(input_dev, EV_KEY,
|
||||
xpad_btn_pad[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1675,7 +1679,8 @@ static int xpad_init_input(struct usb_xpad *xpad)
|
||||
|
||||
if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
|
||||
for (i = 0; xpad_btn_triggers[i] >= 0; i++)
|
||||
__set_bit(xpad_btn_triggers[i], input_dev->keybit);
|
||||
input_set_capability(input_dev, EV_KEY,
|
||||
xpad_btn_triggers[i]);
|
||||
} else {
|
||||
for (i = 0; xpad_abs_triggers[i] >= 0; i++)
|
||||
xpad_set_up_abs(input_dev, xpad_abs_triggers[i]);
|
||||
|
@@ -157,15 +157,6 @@ config KEYBOARD_QT2160
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called qt2160.
|
||||
|
||||
config KEYBOARD_BFIN
|
||||
tristate "Blackfin BF54x keypad support"
|
||||
depends on (BF54x && !BF544)
|
||||
help
|
||||
Say Y here if you want to use the BF54x keypad.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called bf54x-keys.
|
||||
|
||||
config KEYBOARD_CLPS711X
|
||||
tristate "CLPS711X Keypad support"
|
||||
depends on OF_GPIO && (ARCH_CLPS711X || COMPILE_TEST)
|
||||
|
@@ -13,7 +13,6 @@ obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
|
||||
obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
|
||||
obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
|
||||
obj-$(CONFIG_KEYBOARD_BCM) += bcm-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_CAP11XX) += cap11xx.o
|
||||
obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_CROS_EC) += cros_ec_keyb.o
|
||||
|
@@ -1,396 +0,0 @@
|
||||
/*
|
||||
* File: drivers/input/keyboard/bf54x-keys.c
|
||||
* Based on:
|
||||
* Author: Michael Hennerich <hennerich@blackfin.uclinux.org>
|
||||
*
|
||||
* Created:
|
||||
* Description: keypad driver for Analog Devices Blackfin BF54x Processors
|
||||
*
|
||||
*
|
||||
* Modified:
|
||||
* Copyright 2007-2008 Analog Devices Inc.
|
||||
*
|
||||
* Bugs: Enter bugs at http://blackfin.uclinux.org/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see the file COPYING, or write
|
||||
* to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#include <asm/portmux.h>
|
||||
#include <mach/bf54x_keys.h>
|
||||
|
||||
#define DRV_NAME "bf54x-keys"
|
||||
#define TIME_SCALE 100 /* 100 ns */
|
||||
#define MAX_MULT (0xFF * TIME_SCALE)
|
||||
#define MAX_RC 8 /* Max Row/Col */
|
||||
|
||||
static const u16 per_rows[] = {
|
||||
P_KEY_ROW7,
|
||||
P_KEY_ROW6,
|
||||
P_KEY_ROW5,
|
||||
P_KEY_ROW4,
|
||||
P_KEY_ROW3,
|
||||
P_KEY_ROW2,
|
||||
P_KEY_ROW1,
|
||||
P_KEY_ROW0,
|
||||
0
|
||||
};
|
||||
|
||||
static const u16 per_cols[] = {
|
||||
P_KEY_COL7,
|
||||
P_KEY_COL6,
|
||||
P_KEY_COL5,
|
||||
P_KEY_COL4,
|
||||
P_KEY_COL3,
|
||||
P_KEY_COL2,
|
||||
P_KEY_COL1,
|
||||
P_KEY_COL0,
|
||||
0
|
||||
};
|
||||
|
||||
struct bf54x_kpad {
|
||||
struct input_dev *input;
|
||||
int irq;
|
||||
unsigned short lastkey;
|
||||
unsigned short *keycode;
|
||||
struct timer_list timer;
|
||||
unsigned int keyup_test_jiffies;
|
||||
unsigned short kpad_msel;
|
||||
unsigned short kpad_prescale;
|
||||
unsigned short kpad_ctl;
|
||||
};
|
||||
|
||||
static inline int bfin_kpad_find_key(struct bf54x_kpad *bf54x_kpad,
|
||||
struct input_dev *input, u16 keyident)
|
||||
{
|
||||
u16 i;
|
||||
|
||||
for (i = 0; i < input->keycodemax; i++)
|
||||
if (bf54x_kpad->keycode[i + input->keycodemax] == keyident)
|
||||
return bf54x_kpad->keycode[i];
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline void bfin_keycodecpy(unsigned short *keycode,
|
||||
const unsigned int *pdata_kc,
|
||||
unsigned short keymapsize)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < keymapsize; i++) {
|
||||
keycode[i] = pdata_kc[i] & 0xffff;
|
||||
keycode[i + keymapsize] = pdata_kc[i] >> 16;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 bfin_kpad_get_prescale(u32 timescale)
|
||||
{
|
||||
u32 sclk = get_sclk();
|
||||
|
||||
return ((((sclk / 1000) * timescale) / 1024) - 1);
|
||||
}
|
||||
|
||||
static inline u16 bfin_kpad_get_keypressed(struct bf54x_kpad *bf54x_kpad)
|
||||
{
|
||||
return (bfin_read_KPAD_STAT() & KPAD_PRESSED);
|
||||
}
|
||||
|
||||
static inline void bfin_kpad_clear_irq(void)
|
||||
{
|
||||
bfin_write_KPAD_STAT(0xFFFF);
|
||||
bfin_write_KPAD_ROWCOL(0xFFFF);
|
||||
}
|
||||
|
||||
static void bfin_kpad_timer(struct timer_list *t)
|
||||
{
|
||||
struct bf54x_kpad *bf54x_kpad = from_timer(bf54x_kpad, t, timer);
|
||||
|
||||
if (bfin_kpad_get_keypressed(bf54x_kpad)) {
|
||||
/* Try again later */
|
||||
mod_timer(&bf54x_kpad->timer,
|
||||
jiffies + bf54x_kpad->keyup_test_jiffies);
|
||||
return;
|
||||
}
|
||||
|
||||
input_report_key(bf54x_kpad->input, bf54x_kpad->lastkey, 0);
|
||||
input_sync(bf54x_kpad->input);
|
||||
|
||||
/* Clear IRQ Status */
|
||||
|
||||
bfin_kpad_clear_irq();
|
||||
enable_irq(bf54x_kpad->irq);
|
||||
}
|
||||
|
||||
static irqreturn_t bfin_kpad_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct platform_device *pdev = dev_id;
|
||||
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
|
||||
struct input_dev *input = bf54x_kpad->input;
|
||||
int key;
|
||||
u16 rowcol = bfin_read_KPAD_ROWCOL();
|
||||
|
||||
key = bfin_kpad_find_key(bf54x_kpad, input, rowcol);
|
||||
|
||||
input_report_key(input, key, 1);
|
||||
input_sync(input);
|
||||
|
||||
if (bfin_kpad_get_keypressed(bf54x_kpad)) {
|
||||
disable_irq_nosync(bf54x_kpad->irq);
|
||||
bf54x_kpad->lastkey = key;
|
||||
mod_timer(&bf54x_kpad->timer,
|
||||
jiffies + bf54x_kpad->keyup_test_jiffies);
|
||||
} else {
|
||||
input_report_key(input, key, 0);
|
||||
input_sync(input);
|
||||
|
||||
bfin_kpad_clear_irq();
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int bfin_kpad_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct bf54x_kpad *bf54x_kpad;
|
||||
struct bfin_kpad_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct input_dev *input;
|
||||
int i, error;
|
||||
|
||||
if (!pdata->rows || !pdata->cols || !pdata->keymap) {
|
||||
dev_err(&pdev->dev, "no rows, cols or keymap from pdata\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!pdata->keymapsize ||
|
||||
pdata->keymapsize > (pdata->rows * pdata->cols)) {
|
||||
dev_err(&pdev->dev, "invalid keymapsize\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bf54x_kpad = kzalloc(sizeof(struct bf54x_kpad), GFP_KERNEL);
|
||||
if (!bf54x_kpad)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, bf54x_kpad);
|
||||
|
||||
/* Allocate memory for keymap followed by private LUT */
|
||||
bf54x_kpad->keycode = kmalloc(pdata->keymapsize *
|
||||
sizeof(unsigned short) * 2, GFP_KERNEL);
|
||||
if (!bf54x_kpad->keycode) {
|
||||
error = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!pdata->debounce_time || pdata->debounce_time > MAX_MULT ||
|
||||
!pdata->coldrive_time || pdata->coldrive_time > MAX_MULT) {
|
||||
dev_warn(&pdev->dev,
|
||||
"invalid platform debounce/columndrive time\n");
|
||||
bfin_write_KPAD_MSEL(0xFF0); /* Default MSEL */
|
||||
} else {
|
||||
bfin_write_KPAD_MSEL(
|
||||
((pdata->debounce_time / TIME_SCALE)
|
||||
& DBON_SCALE) |
|
||||
(((pdata->coldrive_time / TIME_SCALE) << 8)
|
||||
& COLDRV_SCALE));
|
||||
|
||||
}
|
||||
|
||||
if (!pdata->keyup_test_interval)
|
||||
bf54x_kpad->keyup_test_jiffies = msecs_to_jiffies(50);
|
||||
else
|
||||
bf54x_kpad->keyup_test_jiffies =
|
||||
msecs_to_jiffies(pdata->keyup_test_interval);
|
||||
|
||||
if (peripheral_request_list((u16 *)&per_rows[MAX_RC - pdata->rows],
|
||||
DRV_NAME)) {
|
||||
dev_err(&pdev->dev, "requesting peripherals failed\n");
|
||||
error = -EFAULT;
|
||||
goto out0;
|
||||
}
|
||||
|
||||
if (peripheral_request_list((u16 *)&per_cols[MAX_RC - pdata->cols],
|
||||
DRV_NAME)) {
|
||||
dev_err(&pdev->dev, "requesting peripherals failed\n");
|
||||
error = -EFAULT;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
bf54x_kpad->irq = platform_get_irq(pdev, 0);
|
||||
if (bf54x_kpad->irq < 0) {
|
||||
error = -ENODEV;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
error = request_irq(bf54x_kpad->irq, bfin_kpad_isr,
|
||||
0, DRV_NAME, pdev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "unable to claim irq %d\n",
|
||||
bf54x_kpad->irq);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
input = input_allocate_device();
|
||||
if (!input) {
|
||||
error = -ENOMEM;
|
||||
goto out3;
|
||||
}
|
||||
|
||||
bf54x_kpad->input = input;
|
||||
|
||||
input->name = pdev->name;
|
||||
input->phys = "bf54x-keys/input0";
|
||||
input->dev.parent = &pdev->dev;
|
||||
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->id.vendor = 0x0001;
|
||||
input->id.product = 0x0001;
|
||||
input->id.version = 0x0100;
|
||||
|
||||
input->keycodesize = sizeof(unsigned short);
|
||||
input->keycodemax = pdata->keymapsize;
|
||||
input->keycode = bf54x_kpad->keycode;
|
||||
|
||||
bfin_keycodecpy(bf54x_kpad->keycode, pdata->keymap, pdata->keymapsize);
|
||||
|
||||
/* setup input device */
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
|
||||
if (pdata->repeat)
|
||||
__set_bit(EV_REP, input->evbit);
|
||||
|
||||
for (i = 0; i < input->keycodemax; i++)
|
||||
if (bf54x_kpad->keycode[i] <= KEY_MAX)
|
||||
__set_bit(bf54x_kpad->keycode[i], input->keybit);
|
||||
__clear_bit(KEY_RESERVED, input->keybit);
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "unable to register input device\n");
|
||||
goto out4;
|
||||
}
|
||||
|
||||
/* Init Keypad Key Up/Release test timer */
|
||||
|
||||
timer_setup(&bf54x_kpad->timer, bfin_kpad_timer, 0);
|
||||
|
||||
bfin_write_KPAD_PRESCALE(bfin_kpad_get_prescale(TIME_SCALE));
|
||||
|
||||
bfin_write_KPAD_CTL((((pdata->cols - 1) << 13) & KPAD_COLEN) |
|
||||
(((pdata->rows - 1) << 10) & KPAD_ROWEN) |
|
||||
(2 & KPAD_IRQMODE));
|
||||
|
||||
bfin_write_KPAD_CTL(bfin_read_KPAD_CTL() | KPAD_EN);
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
return 0;
|
||||
|
||||
out4:
|
||||
input_free_device(input);
|
||||
out3:
|
||||
free_irq(bf54x_kpad->irq, pdev);
|
||||
out2:
|
||||
peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]);
|
||||
out1:
|
||||
peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]);
|
||||
out0:
|
||||
kfree(bf54x_kpad->keycode);
|
||||
out:
|
||||
kfree(bf54x_kpad);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int bfin_kpad_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct bfin_kpad_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
|
||||
|
||||
del_timer_sync(&bf54x_kpad->timer);
|
||||
free_irq(bf54x_kpad->irq, pdev);
|
||||
|
||||
input_unregister_device(bf54x_kpad->input);
|
||||
|
||||
peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]);
|
||||
peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]);
|
||||
|
||||
kfree(bf54x_kpad->keycode);
|
||||
kfree(bf54x_kpad);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int bfin_kpad_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
{
|
||||
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
|
||||
|
||||
bf54x_kpad->kpad_msel = bfin_read_KPAD_MSEL();
|
||||
bf54x_kpad->kpad_prescale = bfin_read_KPAD_PRESCALE();
|
||||
bf54x_kpad->kpad_ctl = bfin_read_KPAD_CTL();
|
||||
|
||||
if (device_may_wakeup(&pdev->dev))
|
||||
enable_irq_wake(bf54x_kpad->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bfin_kpad_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
|
||||
|
||||
bfin_write_KPAD_MSEL(bf54x_kpad->kpad_msel);
|
||||
bfin_write_KPAD_PRESCALE(bf54x_kpad->kpad_prescale);
|
||||
bfin_write_KPAD_CTL(bf54x_kpad->kpad_ctl);
|
||||
|
||||
if (device_may_wakeup(&pdev->dev))
|
||||
disable_irq_wake(bf54x_kpad->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
# define bfin_kpad_suspend NULL
|
||||
# define bfin_kpad_resume NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver bfin_kpad_device_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
},
|
||||
.probe = bfin_kpad_probe,
|
||||
.remove = bfin_kpad_remove,
|
||||
.suspend = bfin_kpad_suspend,
|
||||
.resume = bfin_kpad_resume,
|
||||
};
|
||||
module_platform_driver(bfin_kpad_device_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Keypad driver for BF54x Processors");
|
||||
MODULE_ALIAS("platform:bf54x-keys");
|
@@ -244,24 +244,35 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
|
||||
|
||||
switch (ckdev->ec->event_data.event_type) {
|
||||
case EC_MKBP_EVENT_KEY_MATRIX:
|
||||
/*
|
||||
* If EC is not the wake source, discard key state changes
|
||||
* during suspend.
|
||||
*/
|
||||
if (queued_during_suspend)
|
||||
return NOTIFY_OK;
|
||||
if (device_may_wakeup(ckdev->dev)) {
|
||||
pm_wakeup_event(ckdev->dev, 0);
|
||||
} else {
|
||||
/*
|
||||
* If keyboard is not wake enabled, discard key state
|
||||
* changes during suspend. Switches will be re-checked
|
||||
* in cros_ec_keyb_resume() to be sure nothing is lost.
|
||||
*/
|
||||
if (queued_during_suspend)
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
if (ckdev->ec->event_size != ckdev->cols) {
|
||||
dev_err(ckdev->dev,
|
||||
"Discarded incomplete key matrix event.\n");
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
cros_ec_keyb_process(ckdev,
|
||||
ckdev->ec->event_data.data.key_matrix,
|
||||
ckdev->ec->event_size);
|
||||
break;
|
||||
|
||||
case EC_MKBP_EVENT_SYSRQ:
|
||||
if (device_may_wakeup(ckdev->dev))
|
||||
pm_wakeup_event(ckdev->dev, 0);
|
||||
else if (queued_during_suspend)
|
||||
return NOTIFY_OK;
|
||||
|
||||
val = get_unaligned_le32(&ckdev->ec->event_data.data.sysrq);
|
||||
dev_dbg(ckdev->dev, "sysrq code from EC: %#x\n", val);
|
||||
handle_sysrq(val);
|
||||
@@ -269,12 +280,9 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
|
||||
|
||||
case EC_MKBP_EVENT_BUTTON:
|
||||
case EC_MKBP_EVENT_SWITCH:
|
||||
/*
|
||||
* If EC is not the wake source, discard key state
|
||||
* changes during suspend. Switches will be re-checked in
|
||||
* cros_ec_keyb_resume() to be sure nothing is lost.
|
||||
*/
|
||||
if (queued_during_suspend)
|
||||
if (device_may_wakeup(ckdev->dev))
|
||||
pm_wakeup_event(ckdev->dev, 0);
|
||||
else if (queued_during_suspend)
|
||||
return NOTIFY_OK;
|
||||
|
||||
if (ckdev->ec->event_data.event_type == EC_MKBP_EVENT_BUTTON) {
|
||||
@@ -639,6 +647,7 @@ static int cros_ec_keyb_probe(struct platform_device *pdev)
|
||||
return err;
|
||||
}
|
||||
|
||||
device_init_wakeup(ckdev->dev, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -655,15 +655,6 @@ config INPUT_DM355EVM
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called dm355evm_keys.
|
||||
|
||||
config INPUT_BFIN_ROTARY
|
||||
tristate "Blackfin Rotary support"
|
||||
depends on BF54x || BF52x
|
||||
help
|
||||
Say Y here if you want to use the Blackfin Rotary.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called bfin-rotary.
|
||||
|
||||
config INPUT_WM831X_ON
|
||||
tristate "WM831X ON pin"
|
||||
depends on MFD_WM831X
|
||||
|
@@ -19,7 +19,6 @@ obj-$(CONFIG_INPUT_ARIZONA_HAPTICS) += arizona-haptics.o
|
||||
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
|
||||
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
|
||||
obj-$(CONFIG_INPUT_ATMEL_CAPTOUCH) += atmel_captouch.o
|
||||
obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o
|
||||
obj-$(CONFIG_INPUT_BMA150) += bma150.o
|
||||
obj-$(CONFIG_INPUT_CM109) += cm109.o
|
||||
obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o
|
||||
|
@@ -22,7 +22,7 @@ MODULE_LICENSE("GPL");
|
||||
/*
|
||||
* ATI Remote Wonder II Channel Configuration
|
||||
*
|
||||
* The remote control can by assigned one of sixteen "channels" in order to facilitate
|
||||
* The remote control can be assigned one of sixteen "channels" in order to facilitate
|
||||
* the use of multiple remote controls within range of each other.
|
||||
* A remote's "channel" may be altered by pressing and holding the "PC" button for
|
||||
* approximately 3 seconds, after which the button will slowly flash the count of the
|
||||
|
@@ -1,294 +0,0 @@
|
||||
/*
|
||||
* Rotary counter driver for Analog Devices Blackfin Processors
|
||||
*
|
||||
* Copyright 2008-2009 Analog Devices Inc.
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_data/bfin_rotary.h>
|
||||
|
||||
#include <asm/portmux.h>
|
||||
|
||||
#define CNT_CONFIG_OFF 0 /* CNT Config Offset */
|
||||
#define CNT_IMASK_OFF 4 /* CNT Interrupt Mask Offset */
|
||||
#define CNT_STATUS_OFF 8 /* CNT Status Offset */
|
||||
#define CNT_COMMAND_OFF 12 /* CNT Command Offset */
|
||||
#define CNT_DEBOUNCE_OFF 16 /* CNT Debounce Offset */
|
||||
#define CNT_COUNTER_OFF 20 /* CNT Counter Offset */
|
||||
#define CNT_MAX_OFF 24 /* CNT Maximum Count Offset */
|
||||
#define CNT_MIN_OFF 28 /* CNT Minimum Count Offset */
|
||||
|
||||
struct bfin_rot {
|
||||
struct input_dev *input;
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
unsigned int up_key;
|
||||
unsigned int down_key;
|
||||
unsigned int button_key;
|
||||
unsigned int rel_code;
|
||||
|
||||
unsigned short mode;
|
||||
unsigned short debounce;
|
||||
|
||||
unsigned short cnt_config;
|
||||
unsigned short cnt_imask;
|
||||
unsigned short cnt_debounce;
|
||||
};
|
||||
|
||||
static void report_key_event(struct input_dev *input, int keycode)
|
||||
{
|
||||
/* simulate a press-n-release */
|
||||
input_report_key(input, keycode, 1);
|
||||
input_sync(input);
|
||||
input_report_key(input, keycode, 0);
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
static void report_rotary_event(struct bfin_rot *rotary, int delta)
|
||||
{
|
||||
struct input_dev *input = rotary->input;
|
||||
|
||||
if (rotary->up_key) {
|
||||
report_key_event(input,
|
||||
delta > 0 ? rotary->up_key : rotary->down_key);
|
||||
} else {
|
||||
input_report_rel(input, rotary->rel_code, delta);
|
||||
input_sync(input);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t bfin_rotary_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct bfin_rot *rotary = dev_id;
|
||||
int delta;
|
||||
|
||||
switch (readw(rotary->base + CNT_STATUS_OFF)) {
|
||||
|
||||
case ICII:
|
||||
break;
|
||||
|
||||
case UCII:
|
||||
case DCII:
|
||||
delta = readl(rotary->base + CNT_COUNTER_OFF);
|
||||
if (delta)
|
||||
report_rotary_event(rotary, delta);
|
||||
break;
|
||||
|
||||
case CZMII:
|
||||
report_key_event(rotary->input, rotary->button_key);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
writew(W1LCNT_ZERO, rotary->base + CNT_COMMAND_OFF); /* Clear COUNTER */
|
||||
writew(-1, rotary->base + CNT_STATUS_OFF); /* Clear STATUS */
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int bfin_rotary_open(struct input_dev *input)
|
||||
{
|
||||
struct bfin_rot *rotary = input_get_drvdata(input);
|
||||
unsigned short val;
|
||||
|
||||
if (rotary->mode & ROT_DEBE)
|
||||
writew(rotary->debounce & DPRESCALE,
|
||||
rotary->base + CNT_DEBOUNCE_OFF);
|
||||
|
||||
writew(rotary->mode & ~CNTE, rotary->base + CNT_CONFIG_OFF);
|
||||
|
||||
val = UCIE | DCIE;
|
||||
if (rotary->button_key)
|
||||
val |= CZMIE;
|
||||
writew(val, rotary->base + CNT_IMASK_OFF);
|
||||
|
||||
writew(rotary->mode | CNTE, rotary->base + CNT_CONFIG_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bfin_rotary_close(struct input_dev *input)
|
||||
{
|
||||
struct bfin_rot *rotary = input_get_drvdata(input);
|
||||
|
||||
writew(0, rotary->base + CNT_CONFIG_OFF);
|
||||
writew(0, rotary->base + CNT_IMASK_OFF);
|
||||
}
|
||||
|
||||
static void bfin_rotary_free_action(void *data)
|
||||
{
|
||||
peripheral_free_list(data);
|
||||
}
|
||||
|
||||
static int bfin_rotary_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct bfin_rotary_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct bfin_rot *rotary;
|
||||
struct resource *res;
|
||||
struct input_dev *input;
|
||||
int error;
|
||||
|
||||
/* Basic validation */
|
||||
if ((pdata->rotary_up_key && !pdata->rotary_down_key) ||
|
||||
(!pdata->rotary_up_key && pdata->rotary_down_key)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pdata->pin_list) {
|
||||
error = peripheral_request_list(pdata->pin_list,
|
||||
dev_name(dev));
|
||||
if (error) {
|
||||
dev_err(dev, "requesting peripherals failed: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = devm_add_action_or_reset(dev, bfin_rotary_free_action,
|
||||
pdata->pin_list);
|
||||
if (error) {
|
||||
dev_err(dev, "setting cleanup action failed: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
rotary = devm_kzalloc(dev, sizeof(struct bfin_rot), GFP_KERNEL);
|
||||
if (!rotary)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
rotary->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(rotary->base))
|
||||
return PTR_ERR(rotary->base);
|
||||
|
||||
input = devm_input_allocate_device(dev);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
rotary->input = input;
|
||||
|
||||
rotary->up_key = pdata->rotary_up_key;
|
||||
rotary->down_key = pdata->rotary_down_key;
|
||||
rotary->button_key = pdata->rotary_button_key;
|
||||
rotary->rel_code = pdata->rotary_rel_code;
|
||||
|
||||
rotary->mode = pdata->mode;
|
||||
rotary->debounce = pdata->debounce;
|
||||
|
||||
input->name = pdev->name;
|
||||
input->phys = "bfin-rotary/input0";
|
||||
input->dev.parent = dev;
|
||||
|
||||
input_set_drvdata(input, rotary);
|
||||
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->id.vendor = 0x0001;
|
||||
input->id.product = 0x0001;
|
||||
input->id.version = 0x0100;
|
||||
|
||||
input->open = bfin_rotary_open;
|
||||
input->close = bfin_rotary_close;
|
||||
|
||||
if (rotary->up_key) {
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
__set_bit(rotary->up_key, input->keybit);
|
||||
__set_bit(rotary->down_key, input->keybit);
|
||||
} else {
|
||||
__set_bit(EV_REL, input->evbit);
|
||||
__set_bit(rotary->rel_code, input->relbit);
|
||||
}
|
||||
|
||||
if (rotary->button_key) {
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
__set_bit(rotary->button_key, input->keybit);
|
||||
}
|
||||
|
||||
/* Quiesce the device before requesting irq */
|
||||
bfin_rotary_close(input);
|
||||
|
||||
rotary->irq = platform_get_irq(pdev, 0);
|
||||
if (rotary->irq < 0) {
|
||||
dev_err(dev, "No rotary IRQ specified\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
error = devm_request_irq(dev, rotary->irq, bfin_rotary_isr,
|
||||
0, dev_name(dev), rotary);
|
||||
if (error) {
|
||||
dev_err(dev, "unable to claim irq %d; error %d\n",
|
||||
rotary->irq, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(dev, "unable to register input device (%d)\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, rotary);
|
||||
device_init_wakeup(dev, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused bfin_rotary_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct bfin_rot *rotary = platform_get_drvdata(pdev);
|
||||
|
||||
rotary->cnt_config = readw(rotary->base + CNT_CONFIG_OFF);
|
||||
rotary->cnt_imask = readw(rotary->base + CNT_IMASK_OFF);
|
||||
rotary->cnt_debounce = readw(rotary->base + CNT_DEBOUNCE_OFF);
|
||||
|
||||
if (device_may_wakeup(&pdev->dev))
|
||||
enable_irq_wake(rotary->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused bfin_rotary_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct bfin_rot *rotary = platform_get_drvdata(pdev);
|
||||
|
||||
writew(rotary->cnt_debounce, rotary->base + CNT_DEBOUNCE_OFF);
|
||||
writew(rotary->cnt_imask, rotary->base + CNT_IMASK_OFF);
|
||||
writew(rotary->cnt_config & ~CNTE, rotary->base + CNT_CONFIG_OFF);
|
||||
|
||||
if (device_may_wakeup(&pdev->dev))
|
||||
disable_irq_wake(rotary->irq);
|
||||
|
||||
if (rotary->cnt_config & CNTE)
|
||||
writew(rotary->cnt_config, rotary->base + CNT_CONFIG_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(bfin_rotary_pm_ops,
|
||||
bfin_rotary_suspend, bfin_rotary_resume);
|
||||
|
||||
static struct platform_driver bfin_rotary_device_driver = {
|
||||
.probe = bfin_rotary_probe,
|
||||
.driver = {
|
||||
.name = "bfin-rotary",
|
||||
.pm = &bfin_rotary_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(bfin_rotary_device_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Rotary Counter driver for Blackfin Processors");
|
||||
MODULE_ALIAS("platform:bfin-rotary");
|
@@ -133,6 +133,18 @@ config MOUSE_PS2_ELANTECH
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MOUSE_PS2_ELANTECH_SMBUS
|
||||
bool "Elantech PS/2 SMbus companion" if EXPERT
|
||||
default y
|
||||
depends on MOUSE_PS2 && MOUSE_PS2_ELANTECH
|
||||
depends on I2C=y || I2C=MOUSE_PS2
|
||||
select MOUSE_PS2_SMBUS
|
||||
help
|
||||
Say Y here if you have a Elantech touchpad connected to
|
||||
to an SMBus, but enumerated through PS/2.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config MOUSE_PS2_SENTELIC
|
||||
bool "Sentelic Finger Sensing Pad PS/2 protocol extension"
|
||||
depends on MOUSE_PS2
|
||||
|
@@ -2049,14 +2049,11 @@ static int alps_hw_init_v1_v2(struct psmouse *psmouse)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alps_hw_init_v6(struct psmouse *psmouse)
|
||||
/* Must be in passthrough mode when calling this function */
|
||||
static int alps_trackstick_enter_extended_mode_v3_v6(struct psmouse *psmouse)
|
||||
{
|
||||
unsigned char param[2] = {0xC8, 0x14};
|
||||
|
||||
/* Enter passthrough mode to let trackpoint enter 6byte raw mode */
|
||||
if (alps_passthrough_mode_v2(psmouse, true))
|
||||
return -1;
|
||||
|
||||
if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
|
||||
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
|
||||
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
|
||||
@@ -2064,9 +2061,25 @@ static int alps_hw_init_v6(struct psmouse *psmouse)
|
||||
ps2_command(&psmouse->ps2dev, ¶m[1], PSMOUSE_CMD_SETRATE))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alps_hw_init_v6(struct psmouse *psmouse)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Enter passthrough mode to let trackpoint enter 6byte raw mode */
|
||||
if (alps_passthrough_mode_v2(psmouse, true))
|
||||
return -1;
|
||||
|
||||
ret = alps_trackstick_enter_extended_mode_v3_v6(psmouse);
|
||||
|
||||
if (alps_passthrough_mode_v2(psmouse, false))
|
||||
return -1;
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (alps_absolute_mode_v6(psmouse)) {
|
||||
psmouse_err(psmouse, "Failed to enable absolute mode\n");
|
||||
return -1;
|
||||
@@ -2140,10 +2153,18 @@ error:
|
||||
|
||||
static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
int ret = 0;
|
||||
int reg_val;
|
||||
unsigned char param[4];
|
||||
|
||||
/*
|
||||
* We need to configure trackstick to report data for touchpad in
|
||||
* extended format. And also we need to tell touchpad to expect data
|
||||
* from trackstick in extended format. Without this configuration
|
||||
* trackstick packets sent from touchpad are in basic format which is
|
||||
* different from what we expect.
|
||||
*/
|
||||
|
||||
if (alps_passthrough_mode_v3(psmouse, reg_base, true))
|
||||
return -EIO;
|
||||
|
||||
@@ -2161,39 +2182,36 @@ static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base)
|
||||
ret = -ENODEV;
|
||||
} else {
|
||||
psmouse_dbg(psmouse, "trackstick E7 report: %3ph\n", param);
|
||||
|
||||
/*
|
||||
* Not sure what this does, but it is absolutely
|
||||
* essential. Without it, the touchpad does not
|
||||
* work at all and the trackstick just emits normal
|
||||
* PS/2 packets.
|
||||
*/
|
||||
if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
|
||||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
|
||||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
|
||||
alps_command_mode_send_nibble(psmouse, 0x9) ||
|
||||
alps_command_mode_send_nibble(psmouse, 0x4)) {
|
||||
psmouse_err(psmouse,
|
||||
"Error sending magic E6 sequence\n");
|
||||
if (alps_trackstick_enter_extended_mode_v3_v6(psmouse)) {
|
||||
psmouse_err(psmouse, "Failed to enter into trackstick extended mode\n");
|
||||
ret = -EIO;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (alps_passthrough_mode_v3(psmouse, reg_base, false))
|
||||
return -EIO;
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (alps_enter_command_mode(psmouse))
|
||||
return -EIO;
|
||||
|
||||
reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x08);
|
||||
if (reg_val == -1) {
|
||||
ret = -EIO;
|
||||
} else {
|
||||
/*
|
||||
* This ensures the trackstick packets are in the format
|
||||
* supported by this driver. If bit 1 isn't set the packet
|
||||
* format is different.
|
||||
* Tell touchpad that trackstick is now in extended mode.
|
||||
* If bit 1 isn't set the packet format is different.
|
||||
*/
|
||||
if (alps_enter_command_mode(psmouse) ||
|
||||
alps_command_mode_write_reg(psmouse,
|
||||
reg_base + 0x08, 0x82) ||
|
||||
alps_exit_command_mode(psmouse))
|
||||
reg_val |= BIT(1);
|
||||
if (__alps_command_mode_write_reg(psmouse, reg_val))
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
error:
|
||||
if (alps_passthrough_mode_v3(psmouse, reg_base, false))
|
||||
ret = -EIO;
|
||||
if (alps_exit_command_mode(psmouse))
|
||||
return -EIO;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@@ -36,6 +36,7 @@
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
@@ -51,6 +52,7 @@
|
||||
#define ETP_MAX_FINGERS 5
|
||||
#define ETP_FINGER_DATA_LEN 5
|
||||
#define ETP_REPORT_ID 0x5D
|
||||
#define ETP_TP_REPORT_ID 0x5E
|
||||
#define ETP_REPORT_ID_OFFSET 2
|
||||
#define ETP_TOUCH_INFO_OFFSET 3
|
||||
#define ETP_FINGER_DATA_OFFSET 4
|
||||
@@ -61,6 +63,7 @@
|
||||
struct elan_tp_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
struct input_dev *tp_input; /* trackpoint input node */
|
||||
struct regulator *vcc;
|
||||
|
||||
const struct elan_transport_ops *ops;
|
||||
@@ -930,6 +933,33 @@ static void elan_report_absolute(struct elan_tp_data *data, u8 *packet)
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
static void elan_report_trackpoint(struct elan_tp_data *data, u8 *report)
|
||||
{
|
||||
struct input_dev *input = data->tp_input;
|
||||
u8 *packet = &report[ETP_REPORT_ID_OFFSET + 1];
|
||||
int x, y;
|
||||
|
||||
if (!data->tp_input) {
|
||||
dev_warn_once(&data->client->dev,
|
||||
"received a trackpoint report while no trackpoint device has been created. Please report upstream.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
input_report_key(input, BTN_LEFT, packet[0] & 0x01);
|
||||
input_report_key(input, BTN_RIGHT, packet[0] & 0x02);
|
||||
input_report_key(input, BTN_MIDDLE, packet[0] & 0x04);
|
||||
|
||||
if ((packet[3] & 0x0F) == 0x06) {
|
||||
x = packet[4] - (int)((packet[1] ^ 0x80) << 1);
|
||||
y = (int)((packet[2] ^ 0x80) << 1) - packet[5];
|
||||
|
||||
input_report_rel(input, REL_X, x);
|
||||
input_report_rel(input, REL_Y, y);
|
||||
}
|
||||
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
static irqreturn_t elan_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct elan_tp_data *data = dev_id;
|
||||
@@ -951,11 +981,17 @@ static irqreturn_t elan_isr(int irq, void *dev_id)
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
if (report[ETP_REPORT_ID_OFFSET] != ETP_REPORT_ID)
|
||||
switch (report[ETP_REPORT_ID_OFFSET]) {
|
||||
case ETP_REPORT_ID:
|
||||
elan_report_absolute(data, report);
|
||||
break;
|
||||
case ETP_TP_REPORT_ID:
|
||||
elan_report_trackpoint(data, report);
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "invalid report id data (%x)\n",
|
||||
report[ETP_REPORT_ID_OFFSET]);
|
||||
else
|
||||
elan_report_absolute(data, report);
|
||||
}
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
@@ -966,6 +1002,36 @@ out:
|
||||
* Elan initialization functions
|
||||
******************************************************************
|
||||
*/
|
||||
|
||||
static int elan_setup_trackpoint_input_device(struct elan_tp_data *data)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
struct input_dev *input;
|
||||
|
||||
input = devm_input_allocate_device(dev);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
input->name = "Elan TrackPoint";
|
||||
input->id.bustype = BUS_I2C;
|
||||
input->id.vendor = ELAN_VENDOR_ID;
|
||||
input->id.product = data->product_id;
|
||||
input_set_drvdata(input, data);
|
||||
|
||||
input_set_capability(input, EV_REL, REL_X);
|
||||
input_set_capability(input, EV_REL, REL_Y);
|
||||
input_set_capability(input, EV_KEY, BTN_LEFT);
|
||||
input_set_capability(input, EV_KEY, BTN_RIGHT);
|
||||
input_set_capability(input, EV_KEY, BTN_MIDDLE);
|
||||
|
||||
__set_bit(INPUT_PROP_POINTER, input->propbit);
|
||||
__set_bit(INPUT_PROP_POINTING_STICK, input->propbit);
|
||||
|
||||
data->tp_input = input;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int elan_setup_input_device(struct elan_tp_data *data)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
@@ -1140,6 +1206,12 @@ static int elan_probe(struct i2c_client *client,
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (device_property_read_bool(&client->dev, "elan,trackpoint")) {
|
||||
error = elan_setup_trackpoint_input_device(data);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Platform code (ACPI, DTS) should normally set up interrupt
|
||||
* for us, but in case it did not let's fall back to using falling
|
||||
@@ -1177,6 +1249,16 @@ static int elan_probe(struct i2c_client *client,
|
||||
return error;
|
||||
}
|
||||
|
||||
if (data->tp_input) {
|
||||
error = input_register_device(data->tp_input);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"failed to register TrackPoint input device: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Systems using device tree should set up wakeup via DTS,
|
||||
* the rest will configure device as wakeup source by default.
|
||||
|
@@ -14,17 +14,20 @@
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/serio.h>
|
||||
#include <linux/libps2.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include "psmouse.h"
|
||||
#include "elantech.h"
|
||||
#include "elan_i2c.h"
|
||||
|
||||
#define elantech_debug(fmt, ...) \
|
||||
do { \
|
||||
if (etd->debug) \
|
||||
if (etd->info.debug) \
|
||||
psmouse_printk(KERN_DEBUG, psmouse, \
|
||||
fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
@@ -105,7 +108,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
|
||||
if (reg > 0x11 && reg < 0x20)
|
||||
return -1;
|
||||
|
||||
switch (etd->hw_version) {
|
||||
switch (etd->info.hw_version) {
|
||||
case 1:
|
||||
if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_READ) ||
|
||||
ps2_sliced_command(&psmouse->ps2dev, reg) ||
|
||||
@@ -137,7 +140,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
|
||||
|
||||
if (rc)
|
||||
psmouse_err(psmouse, "failed to read register 0x%02x.\n", reg);
|
||||
else if (etd->hw_version != 4)
|
||||
else if (etd->info.hw_version != 4)
|
||||
*val = param[0];
|
||||
else
|
||||
*val = param[1];
|
||||
@@ -160,7 +163,7 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
|
||||
if (reg > 0x11 && reg < 0x20)
|
||||
return -1;
|
||||
|
||||
switch (etd->hw_version) {
|
||||
switch (etd->info.hw_version) {
|
||||
case 1:
|
||||
if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_WRITE) ||
|
||||
ps2_sliced_command(&psmouse->ps2dev, reg) ||
|
||||
@@ -237,7 +240,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
|
||||
unsigned char *packet = psmouse->packet;
|
||||
int fingers;
|
||||
|
||||
if (etd->fw_version < 0x020000) {
|
||||
if (etd->info.fw_version < 0x020000) {
|
||||
/*
|
||||
* byte 0: D U p1 p2 1 p3 R L
|
||||
* byte 1: f 0 th tw x9 x8 y9 y8
|
||||
@@ -252,7 +255,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
|
||||
fingers = (packet[0] & 0xc0) >> 6;
|
||||
}
|
||||
|
||||
if (etd->jumpy_cursor) {
|
||||
if (etd->info.jumpy_cursor) {
|
||||
if (fingers != 1) {
|
||||
etd->single_finger_reports = 0;
|
||||
} else if (etd->single_finger_reports < 2) {
|
||||
@@ -282,8 +285,8 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
|
||||
|
||||
psmouse_report_standard_buttons(dev, packet[0]);
|
||||
|
||||
if (etd->fw_version < 0x020000 &&
|
||||
(etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
|
||||
if (etd->info.fw_version < 0x020000 &&
|
||||
(etd->info.capabilities[0] & ETP_CAP_HAS_ROCKER)) {
|
||||
/* rocker up */
|
||||
input_report_key(dev, BTN_FORWARD, packet[0] & 0x40);
|
||||
/* rocker down */
|
||||
@@ -391,7 +394,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
|
||||
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
|
||||
input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4);
|
||||
psmouse_report_standard_buttons(dev, packet[0]);
|
||||
if (etd->reports_pressure) {
|
||||
if (etd->info.reports_pressure) {
|
||||
input_report_abs(dev, ABS_PRESSURE, pres);
|
||||
input_report_abs(dev, ABS_TOOL_WIDTH, width);
|
||||
}
|
||||
@@ -444,7 +447,7 @@ static void elantech_report_trackpoint(struct psmouse *psmouse,
|
||||
|
||||
default:
|
||||
/* Dump unexpected packet sequences if debug=1 (default) */
|
||||
if (etd->debug == 1)
|
||||
if (etd->info.debug == 1)
|
||||
elantech_packet_dump(psmouse);
|
||||
|
||||
break;
|
||||
@@ -523,7 +526,7 @@ static void elantech_report_absolute_v3(struct psmouse *psmouse,
|
||||
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
|
||||
|
||||
/* For clickpads map both buttons to BTN_LEFT */
|
||||
if (etd->fw_version & 0x001000)
|
||||
if (etd->info.fw_version & 0x001000)
|
||||
input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
|
||||
else
|
||||
psmouse_report_standard_buttons(dev, packet[0]);
|
||||
@@ -541,7 +544,7 @@ static void elantech_input_sync_v4(struct psmouse *psmouse)
|
||||
unsigned char *packet = psmouse->packet;
|
||||
|
||||
/* For clickpads map both buttons to BTN_LEFT */
|
||||
if (etd->fw_version & 0x001000)
|
||||
if (etd->info.fw_version & 0x001000)
|
||||
input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
|
||||
else
|
||||
psmouse_report_standard_buttons(dev, packet[0]);
|
||||
@@ -669,7 +672,7 @@ static int elantech_packet_check_v1(struct psmouse *psmouse)
|
||||
unsigned char p1, p2, p3;
|
||||
|
||||
/* Parity bits are placed differently */
|
||||
if (etd->fw_version < 0x020000) {
|
||||
if (etd->info.fw_version < 0x020000) {
|
||||
/* byte 0: D U p1 p2 1 p3 R L */
|
||||
p1 = (packet[0] & 0x20) >> 5;
|
||||
p2 = (packet[0] & 0x10) >> 4;
|
||||
@@ -714,7 +717,7 @@ static int elantech_packet_check_v2(struct psmouse *psmouse)
|
||||
* With all three cases, if the constant bits are not exactly what I
|
||||
* expected, I consider them invalid.
|
||||
*/
|
||||
if (etd->reports_pressure)
|
||||
if (etd->info.reports_pressure)
|
||||
return (packet[0] & 0x0c) == 0x04 &&
|
||||
(packet[3] & 0x0f) == 0x02;
|
||||
|
||||
@@ -751,7 +754,7 @@ static int elantech_packet_check_v3(struct psmouse *psmouse)
|
||||
* If the hardware flag 'crc_enabled' is set the packets have
|
||||
* different signatures.
|
||||
*/
|
||||
if (etd->crc_enabled) {
|
||||
if (etd->info.crc_enabled) {
|
||||
if ((packet[3] & 0x09) == 0x08)
|
||||
return PACKET_V3_HEAD;
|
||||
|
||||
@@ -782,7 +785,7 @@ static int elantech_packet_check_v4(struct psmouse *psmouse)
|
||||
return PACKET_TRACKPOINT;
|
||||
|
||||
/* This represents the version of IC body. */
|
||||
ic_version = (etd->fw_version & 0x0f0000) >> 16;
|
||||
ic_version = (etd->info.fw_version & 0x0f0000) >> 16;
|
||||
|
||||
/*
|
||||
* Sanity check based on the constant bits of a packet.
|
||||
@@ -791,9 +794,9 @@ static int elantech_packet_check_v4(struct psmouse *psmouse)
|
||||
* the IC body, but are the same for every packet,
|
||||
* regardless of the type.
|
||||
*/
|
||||
if (etd->crc_enabled)
|
||||
if (etd->info.crc_enabled)
|
||||
sanity_check = ((packet[3] & 0x08) == 0x00);
|
||||
else if (ic_version == 7 && etd->samples[1] == 0x2A)
|
||||
else if (ic_version == 7 && etd->info.samples[1] == 0x2A)
|
||||
sanity_check = ((packet[3] & 0x1c) == 0x10);
|
||||
else
|
||||
sanity_check = ((packet[0] & 0x0c) == 0x04 &&
|
||||
@@ -827,12 +830,12 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
|
||||
if (psmouse->pktcnt < psmouse->pktsize)
|
||||
return PSMOUSE_GOOD_DATA;
|
||||
|
||||
if (etd->debug > 1)
|
||||
if (etd->info.debug > 1)
|
||||
elantech_packet_dump(psmouse);
|
||||
|
||||
switch (etd->hw_version) {
|
||||
switch (etd->info.hw_version) {
|
||||
case 1:
|
||||
if (etd->paritycheck && !elantech_packet_check_v1(psmouse))
|
||||
if (etd->info.paritycheck && !elantech_packet_check_v1(psmouse))
|
||||
return PSMOUSE_BAD_DATA;
|
||||
|
||||
elantech_report_absolute_v1(psmouse);
|
||||
@@ -843,7 +846,7 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
|
||||
if (elantech_debounce_check_v2(psmouse))
|
||||
return PSMOUSE_FULL_PACKET;
|
||||
|
||||
if (etd->paritycheck && !elantech_packet_check_v2(psmouse))
|
||||
if (etd->info.paritycheck && !elantech_packet_check_v2(psmouse))
|
||||
return PSMOUSE_BAD_DATA;
|
||||
|
||||
elantech_report_absolute_v2(psmouse);
|
||||
@@ -916,7 +919,7 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
|
||||
int tries = ETP_READ_BACK_TRIES;
|
||||
int rc = 0;
|
||||
|
||||
switch (etd->hw_version) {
|
||||
switch (etd->info.hw_version) {
|
||||
case 1:
|
||||
etd->reg_10 = 0x16;
|
||||
etd->reg_11 = 0x8f;
|
||||
@@ -939,7 +942,7 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (etd->set_hw_resolution)
|
||||
if (etd->info.set_hw_resolution)
|
||||
etd->reg_10 = 0x0b;
|
||||
else
|
||||
etd->reg_10 = 0x01;
|
||||
@@ -976,7 +979,7 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
|
||||
if (rc) {
|
||||
psmouse_err(psmouse,
|
||||
"failed to read back register 0x10.\n");
|
||||
} else if (etd->hw_version == 1 &&
|
||||
} else if (etd->info.hw_version == 1 &&
|
||||
!(val & ETP_R10_ABSOLUTE_MODE)) {
|
||||
psmouse_err(psmouse,
|
||||
"touchpad refuses to switch to absolute mode.\n");
|
||||
@@ -997,10 +1000,11 @@ static int elantech_set_range(struct psmouse *psmouse,
|
||||
unsigned int *width)
|
||||
{
|
||||
struct elantech_data *etd = psmouse->private;
|
||||
struct elantech_device_info *info = &etd->info;
|
||||
unsigned char param[3];
|
||||
unsigned char traces;
|
||||
|
||||
switch (etd->hw_version) {
|
||||
switch (info->hw_version) {
|
||||
case 1:
|
||||
*x_min = ETP_XMIN_V1;
|
||||
*y_min = ETP_YMIN_V1;
|
||||
@@ -1009,9 +1013,9 @@ static int elantech_set_range(struct psmouse *psmouse,
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (etd->fw_version == 0x020800 ||
|
||||
etd->fw_version == 0x020b00 ||
|
||||
etd->fw_version == 0x020030) {
|
||||
if (info->fw_version == 0x020800 ||
|
||||
info->fw_version == 0x020b00 ||
|
||||
info->fw_version == 0x020030) {
|
||||
*x_min = ETP_XMIN_V2;
|
||||
*y_min = ETP_YMIN_V2;
|
||||
*x_max = ETP_XMAX_V2;
|
||||
@@ -1020,35 +1024,35 @@ static int elantech_set_range(struct psmouse *psmouse,
|
||||
int i;
|
||||
int fixed_dpi;
|
||||
|
||||
i = (etd->fw_version > 0x020800 &&
|
||||
etd->fw_version < 0x020900) ? 1 : 2;
|
||||
i = (info->fw_version > 0x020800 &&
|
||||
info->fw_version < 0x020900) ? 1 : 2;
|
||||
|
||||
if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
|
||||
if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
|
||||
return -1;
|
||||
|
||||
fixed_dpi = param[1] & 0x10;
|
||||
|
||||
if (((etd->fw_version >> 16) == 0x14) && fixed_dpi) {
|
||||
if (etd->send_cmd(psmouse, ETP_SAMPLE_QUERY, param))
|
||||
if (((info->fw_version >> 16) == 0x14) && fixed_dpi) {
|
||||
if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, param))
|
||||
return -1;
|
||||
|
||||
*x_max = (etd->capabilities[1] - i) * param[1] / 2;
|
||||
*y_max = (etd->capabilities[2] - i) * param[2] / 2;
|
||||
} else if (etd->fw_version == 0x040216) {
|
||||
*x_max = (info->capabilities[1] - i) * param[1] / 2;
|
||||
*y_max = (info->capabilities[2] - i) * param[2] / 2;
|
||||
} else if (info->fw_version == 0x040216) {
|
||||
*x_max = 819;
|
||||
*y_max = 405;
|
||||
} else if (etd->fw_version == 0x040219 || etd->fw_version == 0x040215) {
|
||||
} else if (info->fw_version == 0x040219 || info->fw_version == 0x040215) {
|
||||
*x_max = 900;
|
||||
*y_max = 500;
|
||||
} else {
|
||||
*x_max = (etd->capabilities[1] - i) * 64;
|
||||
*y_max = (etd->capabilities[2] - i) * 64;
|
||||
*x_max = (info->capabilities[1] - i) * 64;
|
||||
*y_max = (info->capabilities[2] - i) * 64;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
|
||||
if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
|
||||
return -1;
|
||||
|
||||
*x_max = (0x0f & param[0]) << 8 | param[1];
|
||||
@@ -1056,12 +1060,12 @@ static int elantech_set_range(struct psmouse *psmouse,
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
|
||||
if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
|
||||
return -1;
|
||||
|
||||
*x_max = (0x0f & param[0]) << 8 | param[1];
|
||||
*y_max = (0xf0 & param[0]) << 4 | param[2];
|
||||
traces = etd->capabilities[1];
|
||||
traces = info->capabilities[1];
|
||||
if ((traces < 2) || (traces > *x_max))
|
||||
return -1;
|
||||
|
||||
@@ -1083,7 +1087,8 @@ static unsigned int elantech_convert_res(unsigned int val)
|
||||
|
||||
static int elantech_get_resolution_v4(struct psmouse *psmouse,
|
||||
unsigned int *x_res,
|
||||
unsigned int *y_res)
|
||||
unsigned int *y_res,
|
||||
unsigned int *bus)
|
||||
{
|
||||
unsigned char param[3];
|
||||
|
||||
@@ -1092,6 +1097,7 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
|
||||
|
||||
*x_res = elantech_convert_res(param[1] & 0x0f);
|
||||
*y_res = elantech_convert_res((param[1] & 0xf0) >> 4);
|
||||
*bus = param[2];
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1140,7 +1146,7 @@ static void elantech_set_buttonpad_prop(struct psmouse *psmouse)
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
struct elantech_data *etd = psmouse->private;
|
||||
|
||||
if (etd->fw_version & 0x001000) {
|
||||
if (etd->info.fw_version & 0x001000) {
|
||||
__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
|
||||
__clear_bit(BTN_RIGHT, dev->keybit);
|
||||
}
|
||||
@@ -1176,8 +1182,8 @@ static int elantech_set_input_params(struct psmouse *psmouse)
|
||||
{
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
struct elantech_data *etd = psmouse->private;
|
||||
struct elantech_device_info *info = &etd->info;
|
||||
unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0, width = 0;
|
||||
unsigned int x_res = 31, y_res = 31;
|
||||
|
||||
if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width))
|
||||
return -1;
|
||||
@@ -1197,11 +1203,11 @@ static int elantech_set_input_params(struct psmouse *psmouse)
|
||||
__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
|
||||
__set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
|
||||
|
||||
switch (etd->hw_version) {
|
||||
switch (info->hw_version) {
|
||||
case 1:
|
||||
/* Rocker button */
|
||||
if (etd->fw_version < 0x020000 &&
|
||||
(etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
|
||||
if (info->fw_version < 0x020000 &&
|
||||
(info->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
|
||||
__set_bit(BTN_FORWARD, dev->keybit);
|
||||
__set_bit(BTN_BACK, dev->keybit);
|
||||
}
|
||||
@@ -1214,11 +1220,11 @@ static int elantech_set_input_params(struct psmouse *psmouse)
|
||||
__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
|
||||
/* fall through */
|
||||
case 3:
|
||||
if (etd->hw_version == 3)
|
||||
if (info->hw_version == 3)
|
||||
elantech_set_buttonpad_prop(psmouse);
|
||||
input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0);
|
||||
input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0);
|
||||
if (etd->reports_pressure) {
|
||||
if (info->reports_pressure) {
|
||||
input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2,
|
||||
ETP_PMAX_V2, 0, 0);
|
||||
input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
|
||||
@@ -1230,13 +1236,6 @@ static int elantech_set_input_params(struct psmouse *psmouse)
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (elantech_get_resolution_v4(psmouse, &x_res, &y_res)) {
|
||||
/*
|
||||
* if query failed, print a warning and leave the values
|
||||
* zero to resemble synaptics.c behavior.
|
||||
*/
|
||||
psmouse_warn(psmouse, "couldn't query resolution data.\n");
|
||||
}
|
||||
elantech_set_buttonpad_prop(psmouse);
|
||||
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
|
||||
/* For X to recognize me as touchpad. */
|
||||
@@ -1265,11 +1264,11 @@ static int elantech_set_input_params(struct psmouse *psmouse)
|
||||
break;
|
||||
}
|
||||
|
||||
input_abs_set_res(dev, ABS_X, x_res);
|
||||
input_abs_set_res(dev, ABS_Y, y_res);
|
||||
if (etd->hw_version > 1) {
|
||||
input_abs_set_res(dev, ABS_MT_POSITION_X, x_res);
|
||||
input_abs_set_res(dev, ABS_MT_POSITION_Y, y_res);
|
||||
input_abs_set_res(dev, ABS_X, info->x_res);
|
||||
input_abs_set_res(dev, ABS_Y, info->y_res);
|
||||
if (info->hw_version > 1) {
|
||||
input_abs_set_res(dev, ABS_MT_POSITION_X, info->x_res);
|
||||
input_abs_set_res(dev, ABS_MT_POSITION_Y, info->y_res);
|
||||
}
|
||||
|
||||
etd->y_max = y_max;
|
||||
@@ -1317,7 +1316,7 @@ static ssize_t elantech_set_int_attr(struct psmouse *psmouse,
|
||||
return err;
|
||||
|
||||
/* Do we need to preserve some bits for version 2 hardware too? */
|
||||
if (etd->hw_version == 1) {
|
||||
if (etd->info.hw_version == 1) {
|
||||
if (attr->reg == 0x10)
|
||||
/* Force absolute mode always on */
|
||||
value |= ETP_R10_ABSOLUTE_MODE;
|
||||
@@ -1337,11 +1336,22 @@ static ssize_t elantech_set_int_attr(struct psmouse *psmouse,
|
||||
.field_offset = offsetof(struct elantech_data, _name), \
|
||||
.reg = _register, \
|
||||
}; \
|
||||
PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \
|
||||
PSMOUSE_DEFINE_ATTR(_name, 0644, \
|
||||
&elantech_attr_##_name, \
|
||||
elantech_show_int_attr, \
|
||||
elantech_set_int_attr)
|
||||
|
||||
#define ELANTECH_INFO_ATTR(_name) \
|
||||
static struct elantech_attr_data elantech_attr_##_name = { \
|
||||
.field_offset = offsetof(struct elantech_data, info) + \
|
||||
offsetof(struct elantech_device_info, _name), \
|
||||
.reg = 0, \
|
||||
}; \
|
||||
PSMOUSE_DEFINE_ATTR(_name, 0644, \
|
||||
&elantech_attr_##_name, \
|
||||
elantech_show_int_attr, \
|
||||
elantech_set_int_attr)
|
||||
|
||||
ELANTECH_INT_ATTR(reg_07, 0x07);
|
||||
ELANTECH_INT_ATTR(reg_10, 0x10);
|
||||
ELANTECH_INT_ATTR(reg_11, 0x11);
|
||||
@@ -1352,9 +1362,9 @@ ELANTECH_INT_ATTR(reg_23, 0x23);
|
||||
ELANTECH_INT_ATTR(reg_24, 0x24);
|
||||
ELANTECH_INT_ATTR(reg_25, 0x25);
|
||||
ELANTECH_INT_ATTR(reg_26, 0x26);
|
||||
ELANTECH_INT_ATTR(debug, 0);
|
||||
ELANTECH_INT_ATTR(paritycheck, 0);
|
||||
ELANTECH_INT_ATTR(crc_enabled, 0);
|
||||
ELANTECH_INFO_ATTR(debug);
|
||||
ELANTECH_INFO_ATTR(paritycheck);
|
||||
ELANTECH_INFO_ATTR(crc_enabled);
|
||||
|
||||
static struct attribute *elantech_attrs[] = {
|
||||
&psmouse_attr_reg_07.dattr.attr,
|
||||
@@ -1469,6 +1479,12 @@ static void elantech_disconnect(struct psmouse *psmouse)
|
||||
{
|
||||
struct elantech_data *etd = psmouse->private;
|
||||
|
||||
/*
|
||||
* We might have left a breadcrumb when trying to
|
||||
* set up SMbus companion.
|
||||
*/
|
||||
psmouse_smbus_cleanup(psmouse);
|
||||
|
||||
if (etd->tp_dev)
|
||||
input_unregister_device(etd->tp_dev);
|
||||
sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
|
||||
@@ -1588,25 +1604,25 @@ static const struct dmi_system_id no_hw_res_dmi_table[] = {
|
||||
/*
|
||||
* determine hardware version and set some properties according to it.
|
||||
*/
|
||||
static int elantech_set_properties(struct elantech_data *etd)
|
||||
static int elantech_set_properties(struct elantech_device_info *info)
|
||||
{
|
||||
/* This represents the version of IC body. */
|
||||
int ver = (etd->fw_version & 0x0f0000) >> 16;
|
||||
int ver = (info->fw_version & 0x0f0000) >> 16;
|
||||
|
||||
/* Early version of Elan touchpads doesn't obey the rule. */
|
||||
if (etd->fw_version < 0x020030 || etd->fw_version == 0x020600)
|
||||
etd->hw_version = 1;
|
||||
if (info->fw_version < 0x020030 || info->fw_version == 0x020600)
|
||||
info->hw_version = 1;
|
||||
else {
|
||||
switch (ver) {
|
||||
case 2:
|
||||
case 4:
|
||||
etd->hw_version = 2;
|
||||
info->hw_version = 2;
|
||||
break;
|
||||
case 5:
|
||||
etd->hw_version = 3;
|
||||
info->hw_version = 3;
|
||||
break;
|
||||
case 6 ... 15:
|
||||
etd->hw_version = 4;
|
||||
info->hw_version = 4;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
@@ -1614,100 +1630,88 @@ static int elantech_set_properties(struct elantech_data *etd)
|
||||
}
|
||||
|
||||
/* decide which send_cmd we're gonna use early */
|
||||
etd->send_cmd = etd->hw_version >= 3 ? elantech_send_cmd :
|
||||
synaptics_send_cmd;
|
||||
info->send_cmd = info->hw_version >= 3 ? elantech_send_cmd :
|
||||
synaptics_send_cmd;
|
||||
|
||||
/* Turn on packet checking by default */
|
||||
etd->paritycheck = 1;
|
||||
info->paritycheck = 1;
|
||||
|
||||
/*
|
||||
* This firmware suffers from misreporting coordinates when
|
||||
* a touch action starts causing the mouse cursor or scrolled page
|
||||
* to jump. Enable a workaround.
|
||||
*/
|
||||
etd->jumpy_cursor =
|
||||
(etd->fw_version == 0x020022 || etd->fw_version == 0x020600);
|
||||
info->jumpy_cursor =
|
||||
(info->fw_version == 0x020022 || info->fw_version == 0x020600);
|
||||
|
||||
if (etd->hw_version > 1) {
|
||||
if (info->hw_version > 1) {
|
||||
/* For now show extra debug information */
|
||||
etd->debug = 1;
|
||||
info->debug = 1;
|
||||
|
||||
if (etd->fw_version >= 0x020800)
|
||||
etd->reports_pressure = true;
|
||||
if (info->fw_version >= 0x020800)
|
||||
info->reports_pressure = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* The signatures of v3 and v4 packets change depending on the
|
||||
* value of this hardware flag.
|
||||
*/
|
||||
etd->crc_enabled = (etd->fw_version & 0x4000) == 0x4000 ||
|
||||
dmi_check_system(elantech_dmi_force_crc_enabled);
|
||||
info->crc_enabled = (info->fw_version & 0x4000) == 0x4000 ||
|
||||
dmi_check_system(elantech_dmi_force_crc_enabled);
|
||||
|
||||
/* Enable real hardware resolution on hw_version 3 ? */
|
||||
etd->set_hw_resolution = !dmi_check_system(no_hw_res_dmi_table);
|
||||
info->set_hw_resolution = !dmi_check_system(no_hw_res_dmi_table);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the touchpad and create sysfs entries
|
||||
*/
|
||||
int elantech_init(struct psmouse *psmouse)
|
||||
static int elantech_query_info(struct psmouse *psmouse,
|
||||
struct elantech_device_info *info)
|
||||
{
|
||||
struct elantech_data *etd;
|
||||
int i;
|
||||
int error = -EINVAL;
|
||||
unsigned char param[3];
|
||||
struct input_dev *tp_dev;
|
||||
|
||||
psmouse->private = etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL);
|
||||
if (!etd)
|
||||
return -ENOMEM;
|
||||
|
||||
psmouse_reset(psmouse);
|
||||
|
||||
etd->parity[0] = 1;
|
||||
for (i = 1; i < 256; i++)
|
||||
etd->parity[i] = etd->parity[i & (i - 1)] ^ 1;
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
/*
|
||||
* Do the version query again so we can store the result
|
||||
*/
|
||||
if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) {
|
||||
psmouse_err(psmouse, "failed to query firmware version.\n");
|
||||
goto init_fail;
|
||||
return -EINVAL;
|
||||
}
|
||||
etd->fw_version = (param[0] << 16) | (param[1] << 8) | param[2];
|
||||
info->fw_version = (param[0] << 16) | (param[1] << 8) | param[2];
|
||||
|
||||
if (elantech_set_properties(etd)) {
|
||||
if (elantech_set_properties(info)) {
|
||||
psmouse_err(psmouse, "unknown hardware version, aborting...\n");
|
||||
goto init_fail;
|
||||
return -EINVAL;
|
||||
}
|
||||
psmouse_info(psmouse,
|
||||
"assuming hardware version %d (with firmware version 0x%02x%02x%02x)\n",
|
||||
etd->hw_version, param[0], param[1], param[2]);
|
||||
info->hw_version, param[0], param[1], param[2]);
|
||||
|
||||
if (etd->send_cmd(psmouse, ETP_CAPABILITIES_QUERY,
|
||||
etd->capabilities)) {
|
||||
if (info->send_cmd(psmouse, ETP_CAPABILITIES_QUERY,
|
||||
info->capabilities)) {
|
||||
psmouse_err(psmouse, "failed to query capabilities.\n");
|
||||
goto init_fail;
|
||||
return -EINVAL;
|
||||
}
|
||||
psmouse_info(psmouse,
|
||||
"Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n",
|
||||
etd->capabilities[0], etd->capabilities[1],
|
||||
etd->capabilities[2]);
|
||||
info->capabilities[0], info->capabilities[1],
|
||||
info->capabilities[2]);
|
||||
|
||||
if (etd->hw_version != 1) {
|
||||
if (etd->send_cmd(psmouse, ETP_SAMPLE_QUERY, etd->samples)) {
|
||||
if (info->hw_version != 1) {
|
||||
if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, info->samples)) {
|
||||
psmouse_err(psmouse, "failed to query sample data\n");
|
||||
goto init_fail;
|
||||
return -EINVAL;
|
||||
}
|
||||
psmouse_info(psmouse,
|
||||
"Elan sample query result %02x, %02x, %02x\n",
|
||||
etd->samples[0], etd->samples[1], etd->samples[2]);
|
||||
info->samples[0],
|
||||
info->samples[1],
|
||||
info->samples[2]);
|
||||
}
|
||||
|
||||
if (etd->samples[1] == 0x74 && etd->hw_version == 0x03) {
|
||||
if (info->samples[1] == 0x74 && info->hw_version == 0x03) {
|
||||
/*
|
||||
* This module has a bug which makes absolute mode
|
||||
* unusable, so let's abort so we'll be using standard
|
||||
@@ -1715,16 +1719,181 @@ int elantech_init(struct psmouse *psmouse)
|
||||
*/
|
||||
psmouse_info(psmouse,
|
||||
"absolute mode broken, forcing standard PS/2 protocol\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* The MSB indicates the presence of the trackpoint */
|
||||
info->has_trackpoint = (info->capabilities[0] & 0x80) == 0x80;
|
||||
|
||||
info->x_res = 31;
|
||||
info->y_res = 31;
|
||||
if (info->hw_version == 4) {
|
||||
if (elantech_get_resolution_v4(psmouse,
|
||||
&info->x_res,
|
||||
&info->y_res,
|
||||
&info->bus)) {
|
||||
psmouse_warn(psmouse,
|
||||
"failed to query resolution data.\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)
|
||||
|
||||
/*
|
||||
* The newest Elantech device can use a secondary bus (over SMBus) which
|
||||
* provides a better bandwidth and allow a better control of the touchpads.
|
||||
* This is used to decide if we need to use this bus or not.
|
||||
*/
|
||||
enum {
|
||||
ELANTECH_SMBUS_NOT_SET = -1,
|
||||
ELANTECH_SMBUS_OFF,
|
||||
ELANTECH_SMBUS_ON,
|
||||
};
|
||||
|
||||
static int elantech_smbus = IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) ?
|
||||
ELANTECH_SMBUS_NOT_SET : ELANTECH_SMBUS_OFF;
|
||||
module_param_named(elantech_smbus, elantech_smbus, int, 0644);
|
||||
MODULE_PARM_DESC(elantech_smbus, "Use a secondary bus for the Elantech device.");
|
||||
|
||||
static int elantech_create_smbus(struct psmouse *psmouse,
|
||||
struct elantech_device_info *info,
|
||||
bool leave_breadcrumbs)
|
||||
{
|
||||
const struct property_entry i2c_properties[] = {
|
||||
PROPERTY_ENTRY_BOOL("elan,trackpoint"),
|
||||
{ },
|
||||
};
|
||||
struct i2c_board_info smbus_board = {
|
||||
I2C_BOARD_INFO("elan_i2c", 0x15),
|
||||
.flags = I2C_CLIENT_HOST_NOTIFY,
|
||||
};
|
||||
|
||||
if (info->has_trackpoint)
|
||||
smbus_board.properties = i2c_properties;
|
||||
|
||||
return psmouse_smbus_init(psmouse, &smbus_board, NULL, 0, false,
|
||||
leave_breadcrumbs);
|
||||
}
|
||||
|
||||
/**
|
||||
* elantech_setup_smbus - called once the PS/2 devices are enumerated
|
||||
* and decides to instantiate a SMBus InterTouch device.
|
||||
*/
|
||||
static int elantech_setup_smbus(struct psmouse *psmouse,
|
||||
struct elantech_device_info *info,
|
||||
bool leave_breadcrumbs)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (elantech_smbus == ELANTECH_SMBUS_OFF)
|
||||
return -ENXIO;
|
||||
|
||||
if (elantech_smbus == ELANTECH_SMBUS_NOT_SET) {
|
||||
/*
|
||||
* New ICs are enabled by default.
|
||||
* Old ICs are up to the user to decide.
|
||||
*/
|
||||
if (!ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version))
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
psmouse_info(psmouse, "Trying to set up SMBus access\n");
|
||||
|
||||
error = elantech_create_smbus(psmouse, info, leave_breadcrumbs);
|
||||
if (error) {
|
||||
if (error == -EAGAIN)
|
||||
psmouse_info(psmouse, "SMbus companion is not ready yet\n");
|
||||
else
|
||||
psmouse_err(psmouse, "unable to create intertouch device\n");
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool elantech_use_host_notify(struct psmouse *psmouse,
|
||||
struct elantech_device_info *info)
|
||||
{
|
||||
if (ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version))
|
||||
return true;
|
||||
|
||||
switch (info->bus) {
|
||||
case ETP_BUS_PS2_ONLY:
|
||||
/* expected case */
|
||||
break;
|
||||
case ETP_BUS_SMB_ALERT_ONLY:
|
||||
/* fall-through */
|
||||
case ETP_BUS_PS2_SMB_ALERT:
|
||||
psmouse_dbg(psmouse, "Ignoring SMBus provider through alert protocol.\n");
|
||||
break;
|
||||
case ETP_BUS_SMB_HST_NTFY_ONLY:
|
||||
/* fall-through */
|
||||
case ETP_BUS_PS2_SMB_HST_NTFY:
|
||||
return true;
|
||||
default:
|
||||
psmouse_dbg(psmouse,
|
||||
"Ignoring SMBus bus provider %d.\n",
|
||||
info->bus);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int elantech_init_smbus(struct psmouse *psmouse)
|
||||
{
|
||||
struct elantech_device_info info;
|
||||
int error = -EINVAL;
|
||||
|
||||
psmouse_reset(psmouse);
|
||||
|
||||
error = elantech_query_info(psmouse, &info);
|
||||
if (error)
|
||||
goto init_fail;
|
||||
|
||||
if (info.hw_version < 4) {
|
||||
error = -ENXIO;
|
||||
goto init_fail;
|
||||
}
|
||||
|
||||
return elantech_create_smbus(psmouse, &info, false);
|
||||
init_fail:
|
||||
psmouse_reset(psmouse);
|
||||
return error;
|
||||
}
|
||||
#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */
|
||||
|
||||
/*
|
||||
* Initialize the touchpad and create sysfs entries
|
||||
*/
|
||||
static int elantech_setup_ps2(struct psmouse *psmouse,
|
||||
struct elantech_device_info *info)
|
||||
{
|
||||
struct elantech_data *etd;
|
||||
int i;
|
||||
int error = -EINVAL;
|
||||
struct input_dev *tp_dev;
|
||||
|
||||
psmouse->private = etd = kzalloc(sizeof(*etd), GFP_KERNEL);
|
||||
if (!etd)
|
||||
return -ENOMEM;
|
||||
|
||||
etd->info = *info;
|
||||
|
||||
etd->parity[0] = 1;
|
||||
for (i = 1; i < 256; i++)
|
||||
etd->parity[i] = etd->parity[i & (i - 1)] ^ 1;
|
||||
|
||||
if (elantech_set_absolute_mode(psmouse)) {
|
||||
psmouse_err(psmouse,
|
||||
"failed to put touchpad into absolute mode.\n");
|
||||
goto init_fail;
|
||||
}
|
||||
|
||||
if (etd->fw_version == 0x381f17) {
|
||||
if (info->fw_version == 0x381f17) {
|
||||
etd->original_set_rate = psmouse->set_rate;
|
||||
psmouse->set_rate = elantech_set_rate_restore_reg_07;
|
||||
}
|
||||
@@ -1743,8 +1912,7 @@ int elantech_init(struct psmouse *psmouse)
|
||||
goto init_fail;
|
||||
}
|
||||
|
||||
/* The MSB indicates the presence of the trackpoint */
|
||||
if ((etd->capabilities[0] & 0x80) == 0x80) {
|
||||
if (info->has_trackpoint) {
|
||||
tp_dev = input_allocate_device();
|
||||
|
||||
if (!tp_dev) {
|
||||
@@ -1780,7 +1948,7 @@ int elantech_init(struct psmouse *psmouse)
|
||||
psmouse->protocol_handler = elantech_process_byte;
|
||||
psmouse->disconnect = elantech_disconnect;
|
||||
psmouse->reconnect = elantech_reconnect;
|
||||
psmouse->pktsize = etd->hw_version > 1 ? 6 : 4;
|
||||
psmouse->pktsize = info->hw_version > 1 ? 6 : 4;
|
||||
|
||||
return 0;
|
||||
init_fail_tp_reg:
|
||||
@@ -1789,7 +1957,70 @@ int elantech_init(struct psmouse *psmouse)
|
||||
sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
|
||||
&elantech_attr_group);
|
||||
init_fail:
|
||||
psmouse_reset(psmouse);
|
||||
kfree(etd);
|
||||
return error;
|
||||
}
|
||||
|
||||
int elantech_init_ps2(struct psmouse *psmouse)
|
||||
{
|
||||
struct elantech_device_info info;
|
||||
int error = -EINVAL;
|
||||
|
||||
psmouse_reset(psmouse);
|
||||
|
||||
error = elantech_query_info(psmouse, &info);
|
||||
if (error)
|
||||
goto init_fail;
|
||||
|
||||
error = elantech_setup_ps2(psmouse, &info);
|
||||
if (error)
|
||||
goto init_fail;
|
||||
|
||||
return 0;
|
||||
init_fail:
|
||||
psmouse_reset(psmouse);
|
||||
return error;
|
||||
}
|
||||
|
||||
int elantech_init(struct psmouse *psmouse)
|
||||
{
|
||||
struct elantech_device_info info;
|
||||
int error = -EINVAL;
|
||||
|
||||
psmouse_reset(psmouse);
|
||||
|
||||
error = elantech_query_info(psmouse, &info);
|
||||
if (error)
|
||||
goto init_fail;
|
||||
|
||||
#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)
|
||||
|
||||
if (elantech_use_host_notify(psmouse, &info)) {
|
||||
if (!IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) ||
|
||||
!IS_ENABLED(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)) {
|
||||
psmouse_warn(psmouse,
|
||||
"The touchpad can support a better bus than the too old PS/2 protocol. "
|
||||
"Make sure MOUSE_PS2_ELANTECH_SMBUS and MOUSE_ELAN_I2C_SMBUS are enabled to get a better touchpad experience.\n");
|
||||
}
|
||||
error = elantech_setup_smbus(psmouse, &info, true);
|
||||
if (!error)
|
||||
return PSMOUSE_ELANTECH_SMBUS;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */
|
||||
|
||||
error = elantech_setup_ps2(psmouse, &info);
|
||||
if (error < 0) {
|
||||
/*
|
||||
* Not using any flavor of Elantech support, so clean up
|
||||
* SMbus breadcrumbs, if any.
|
||||
*/
|
||||
psmouse_smbus_cleanup(psmouse);
|
||||
goto init_fail;
|
||||
}
|
||||
|
||||
return PSMOUSE_ELANTECH;
|
||||
init_fail:
|
||||
psmouse_reset(psmouse);
|
||||
return error;
|
||||
}
|
||||
|
@@ -106,6 +106,30 @@
|
||||
*/
|
||||
#define ETP_WEIGHT_VALUE 5
|
||||
|
||||
/*
|
||||
* Bus information on 3rd byte of query ETP_RESOLUTION_QUERY(0x04)
|
||||
*/
|
||||
#define ETP_BUS_PS2_ONLY 0
|
||||
#define ETP_BUS_SMB_ALERT_ONLY 1
|
||||
#define ETP_BUS_SMB_HST_NTFY_ONLY 2
|
||||
#define ETP_BUS_PS2_SMB_ALERT 3
|
||||
#define ETP_BUS_PS2_SMB_HST_NTFY 4
|
||||
|
||||
/*
|
||||
* New ICs are either using SMBus Host Notify or just plain PS2.
|
||||
*
|
||||
* ETP_FW_VERSION_QUERY is:
|
||||
* Byte 1:
|
||||
* - bit 0..3: IC BODY
|
||||
* Byte 2:
|
||||
* - bit 4: HiddenButton
|
||||
* - bit 5: PS2_SMBUS_NOTIFY
|
||||
* - bit 6: PS2CRCCheck
|
||||
*/
|
||||
#define ETP_NEW_IC_SMBUS_HOST_NOTIFY(fw_version) \
|
||||
((((fw_version) & 0x0f2000) == 0x0f2000) && \
|
||||
((fw_version) & 0x0000ff) > 0)
|
||||
|
||||
/*
|
||||
* The base position for one finger, v4 hardware
|
||||
*/
|
||||
@@ -114,6 +138,25 @@ struct finger_pos {
|
||||
unsigned int y;
|
||||
};
|
||||
|
||||
struct elantech_device_info {
|
||||
unsigned char capabilities[3];
|
||||
unsigned char samples[3];
|
||||
unsigned char debug;
|
||||
unsigned char hw_version;
|
||||
unsigned int fw_version;
|
||||
unsigned int x_res;
|
||||
unsigned int y_res;
|
||||
unsigned int bus;
|
||||
bool paritycheck;
|
||||
bool jumpy_cursor;
|
||||
bool reports_pressure;
|
||||
bool crc_enabled;
|
||||
bool set_hw_resolution;
|
||||
bool has_trackpoint;
|
||||
int (*send_cmd)(struct psmouse *psmouse, unsigned char c,
|
||||
unsigned char *param);
|
||||
};
|
||||
|
||||
struct elantech_data {
|
||||
struct input_dev *tp_dev; /* Relative device for trackpoint */
|
||||
char tp_phys[32];
|
||||
@@ -127,27 +170,18 @@ struct elantech_data {
|
||||
unsigned char reg_24;
|
||||
unsigned char reg_25;
|
||||
unsigned char reg_26;
|
||||
unsigned char debug;
|
||||
unsigned char capabilities[3];
|
||||
unsigned char samples[3];
|
||||
bool paritycheck;
|
||||
bool jumpy_cursor;
|
||||
bool reports_pressure;
|
||||
bool crc_enabled;
|
||||
bool set_hw_resolution;
|
||||
unsigned char hw_version;
|
||||
unsigned int fw_version;
|
||||
unsigned int single_finger_reports;
|
||||
unsigned int y_max;
|
||||
unsigned int width;
|
||||
struct finger_pos mt[ETP_MAX_FINGERS];
|
||||
unsigned char parity[256];
|
||||
int (*send_cmd)(struct psmouse *psmouse, unsigned char c, unsigned char *param);
|
||||
struct elantech_device_info info;
|
||||
void (*original_set_rate)(struct psmouse *psmouse, unsigned int rate);
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MOUSE_PS2_ELANTECH
|
||||
int elantech_detect(struct psmouse *psmouse, bool set_properties);
|
||||
int elantech_init_ps2(struct psmouse *psmouse);
|
||||
int elantech_init(struct psmouse *psmouse);
|
||||
#else
|
||||
static inline int elantech_detect(struct psmouse *psmouse, bool set_properties)
|
||||
@@ -158,6 +192,19 @@ static inline int elantech_init(struct psmouse *psmouse)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
static inline int elantech_init_ps2(struct psmouse *psmouse)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif /* CONFIG_MOUSE_PS2_ELANTECH */
|
||||
|
||||
#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)
|
||||
int elantech_init_smbus(struct psmouse *psmouse);
|
||||
#else
|
||||
static inline int elantech_init_smbus(struct psmouse *psmouse)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */
|
||||
|
||||
#endif
|
||||
|
@@ -856,7 +856,17 @@ static const struct psmouse_protocol psmouse_protocols[] = {
|
||||
.name = "ETPS/2",
|
||||
.alias = "elantech",
|
||||
.detect = elantech_detect,
|
||||
.init = elantech_init,
|
||||
.init = elantech_init_ps2,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_MOUSE_PS2_ELANTECH_SMBUS
|
||||
{
|
||||
.type = PSMOUSE_ELANTECH_SMBUS,
|
||||
.name = "ETSMBus",
|
||||
.alias = "elantech-smbus",
|
||||
.detect = elantech_detect,
|
||||
.init = elantech_init_smbus,
|
||||
.smbus_companion = true,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_MOUSE_PS2_SENTELIC
|
||||
@@ -1158,8 +1168,13 @@ static int psmouse_extensions(struct psmouse *psmouse,
|
||||
/* Try Elantech touchpad */
|
||||
if (max_proto > PSMOUSE_IMEX &&
|
||||
psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH,
|
||||
&max_proto, set_properties, true)) {
|
||||
return PSMOUSE_ELANTECH;
|
||||
&max_proto, set_properties, false)) {
|
||||
if (!set_properties)
|
||||
return PSMOUSE_ELANTECH;
|
||||
|
||||
ret = elantech_init(psmouse);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (max_proto > PSMOUSE_IMEX) {
|
||||
|
@@ -23,6 +23,7 @@ struct psmouse_smbus_dev {
|
||||
struct i2c_client *client;
|
||||
struct list_head node;
|
||||
bool dead;
|
||||
bool need_deactivate;
|
||||
};
|
||||
|
||||
static LIST_HEAD(psmouse_smbus_list);
|
||||
@@ -118,7 +119,10 @@ static psmouse_ret_t psmouse_smbus_process_byte(struct psmouse *psmouse)
|
||||
|
||||
static int psmouse_smbus_reconnect(struct psmouse *psmouse)
|
||||
{
|
||||
psmouse_deactivate(psmouse);
|
||||
struct psmouse_smbus_dev *smbdev = psmouse->private;
|
||||
|
||||
if (smbdev->need_deactivate)
|
||||
psmouse_deactivate(psmouse);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -225,6 +229,7 @@ void psmouse_smbus_cleanup(struct psmouse *psmouse)
|
||||
int psmouse_smbus_init(struct psmouse *psmouse,
|
||||
const struct i2c_board_info *board,
|
||||
const void *pdata, size_t pdata_size,
|
||||
bool need_deactivate,
|
||||
bool leave_breadcrumbs)
|
||||
{
|
||||
struct psmouse_smbus_dev *smbdev;
|
||||
@@ -236,13 +241,20 @@ int psmouse_smbus_init(struct psmouse *psmouse,
|
||||
|
||||
smbdev->psmouse = psmouse;
|
||||
smbdev->board = *board;
|
||||
smbdev->need_deactivate = need_deactivate;
|
||||
|
||||
smbdev->board.platform_data = kmemdup(pdata, pdata_size, GFP_KERNEL);
|
||||
if (!smbdev->board.platform_data) {
|
||||
kfree(smbdev);
|
||||
return -ENOMEM;
|
||||
if (pdata) {
|
||||
smbdev->board.platform_data = kmemdup(pdata, pdata_size,
|
||||
GFP_KERNEL);
|
||||
if (!smbdev->board.platform_data) {
|
||||
kfree(smbdev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_deactivate)
|
||||
psmouse_deactivate(psmouse);
|
||||
|
||||
psmouse->private = smbdev;
|
||||
psmouse->protocol_handler = psmouse_smbus_process_byte;
|
||||
psmouse->reconnect = psmouse_smbus_reconnect;
|
||||
@@ -250,8 +262,6 @@ int psmouse_smbus_init(struct psmouse *psmouse,
|
||||
psmouse->disconnect = psmouse_smbus_disconnect;
|
||||
psmouse->resync_time = 0;
|
||||
|
||||
psmouse_deactivate(psmouse);
|
||||
|
||||
mutex_lock(&psmouse_smbus_mutex);
|
||||
list_add_tail(&smbdev->node, &psmouse_smbus_list);
|
||||
mutex_unlock(&psmouse_smbus_mutex);
|
||||
|
@@ -68,6 +68,7 @@ enum psmouse_type {
|
||||
PSMOUSE_VMMOUSE,
|
||||
PSMOUSE_BYD,
|
||||
PSMOUSE_SYNAPTICS_SMBUS,
|
||||
PSMOUSE_ELANTECH_SMBUS,
|
||||
PSMOUSE_AUTO /* This one should always be last */
|
||||
};
|
||||
|
||||
@@ -224,6 +225,7 @@ struct i2c_board_info;
|
||||
int psmouse_smbus_init(struct psmouse *psmouse,
|
||||
const struct i2c_board_info *board,
|
||||
const void *pdata, size_t pdata_size,
|
||||
bool need_deactivate,
|
||||
bool leave_breadcrumbs);
|
||||
void psmouse_smbus_cleanup(struct psmouse *psmouse);
|
||||
|
||||
|
@@ -1754,7 +1754,7 @@ static int synaptics_create_intertouch(struct psmouse *psmouse,
|
||||
};
|
||||
|
||||
return psmouse_smbus_init(psmouse, &intertouch_board,
|
||||
&pdata, sizeof(pdata),
|
||||
&pdata, sizeof(pdata), true,
|
||||
leave_breadcrumbs);
|
||||
}
|
||||
|
||||
|
@@ -91,7 +91,7 @@ struct gscps2port {
|
||||
struct parisc_device *padev;
|
||||
struct serio *port;
|
||||
spinlock_t lock;
|
||||
char *addr;
|
||||
char __iomem *addr;
|
||||
u8 act, append; /* position in buffer[] */
|
||||
struct {
|
||||
u8 data;
|
||||
@@ -114,7 +114,7 @@ struct gscps2port {
|
||||
* wait_TBE() - wait for Transmit Buffer Empty
|
||||
*/
|
||||
|
||||
static int wait_TBE(char *addr)
|
||||
static int wait_TBE(char __iomem *addr)
|
||||
{
|
||||
int timeout = 25000; /* device is expected to react within 250 msec */
|
||||
while (gscps2_readb_status(addr) & GSC_STAT_TBNE) {
|
||||
@@ -146,14 +146,14 @@ static void gscps2_flush(struct gscps2port *ps2port)
|
||||
static inline int gscps2_writeb_output(struct gscps2port *ps2port, u8 data)
|
||||
{
|
||||
unsigned long flags;
|
||||
char *addr = ps2port->addr;
|
||||
char __iomem *addr = ps2port->addr;
|
||||
|
||||
if (!wait_TBE(addr)) {
|
||||
printk(KERN_DEBUG PFX "timeout - could not write byte %#x\n", data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (gscps2_readb_status(ps2port->addr) & GSC_STAT_RBNE)
|
||||
while (gscps2_readb_status(addr) & GSC_STAT_RBNE)
|
||||
/* wait */;
|
||||
|
||||
spin_lock_irqsave(&ps2port->lock, flags);
|
||||
@@ -200,13 +200,12 @@ static void gscps2_enable(struct gscps2port *ps2port, int enable)
|
||||
|
||||
static void gscps2_reset(struct gscps2port *ps2port)
|
||||
{
|
||||
char *addr = ps2port->addr;
|
||||
unsigned long flags;
|
||||
|
||||
/* reset the interface */
|
||||
spin_lock_irqsave(&ps2port->lock, flags);
|
||||
gscps2_flush(ps2port);
|
||||
writeb(0xff, addr+GSC_RESET);
|
||||
writeb(0xff, ps2port->addr + GSC_RESET);
|
||||
gscps2_flush(ps2port);
|
||||
spin_unlock_irqrestore(&ps2port->lock, flags);
|
||||
}
|
||||
|
@@ -164,6 +164,17 @@ config TOUCHSCREEN_CHIPONE_ICN8318
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called chipone_icn8318.
|
||||
|
||||
config TOUCHSCREEN_CHIPONE_ICN8505
|
||||
tristate "chipone icn8505 touchscreen controller"
|
||||
depends on I2C && ACPI
|
||||
help
|
||||
Say Y here if you have a ChipOne icn8505 based I2C touchscreen.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called chipone_icn8505.
|
||||
|
||||
config TOUCHSCREEN_CY8CTMG110
|
||||
tristate "cy8ctmg110 touchscreen"
|
||||
depends on I2C
|
||||
|
@@ -19,6 +19,7 @@ obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318) += chipone_icn8318.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8505) += chipone_icn8505.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o
|
||||
|
@@ -23,12 +23,13 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_data/atmel_mxt_ts.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/property.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
@@ -193,6 +194,8 @@ enum t100_type {
|
||||
|
||||
/* Delay times */
|
||||
#define MXT_BACKUP_TIME 50 /* msec */
|
||||
#define MXT_RESET_GPIO_TIME 20 /* msec */
|
||||
#define MXT_RESET_INVALID_CHG 100 /* msec */
|
||||
#define MXT_RESET_TIME 200 /* msec */
|
||||
#define MXT_RESET_TIMEOUT 3000 /* msec */
|
||||
#define MXT_CRC_TIMEOUT 1000 /* msec */
|
||||
@@ -268,12 +271,16 @@ static const struct v4l2_file_operations mxt_video_fops = {
|
||||
.poll = vb2_fop_poll,
|
||||
};
|
||||
|
||||
enum mxt_suspend_mode {
|
||||
MXT_SUSPEND_DEEP_SLEEP = 0,
|
||||
MXT_SUSPEND_T9_CTRL = 1,
|
||||
};
|
||||
|
||||
/* Each client has this additional data */
|
||||
struct mxt_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input_dev;
|
||||
char phys[64]; /* device physical location */
|
||||
const struct mxt_platform_data *pdata;
|
||||
struct mxt_object *object_table;
|
||||
struct mxt_info *info;
|
||||
void *raw_info_block;
|
||||
@@ -325,6 +332,11 @@ struct mxt_data {
|
||||
|
||||
/* for config update handling */
|
||||
struct completion crc_completion;
|
||||
|
||||
u32 *t19_keymap;
|
||||
unsigned int t19_num_keys;
|
||||
|
||||
enum mxt_suspend_mode suspend_mode;
|
||||
};
|
||||
|
||||
struct mxt_vb2_buffer {
|
||||
@@ -744,15 +756,14 @@ static int mxt_write_object(struct mxt_data *data,
|
||||
static void mxt_input_button(struct mxt_data *data, u8 *message)
|
||||
{
|
||||
struct input_dev *input = data->input_dev;
|
||||
const struct mxt_platform_data *pdata = data->pdata;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pdata->t19_num_keys; i++) {
|
||||
if (pdata->t19_keymap[i] == KEY_RESERVED)
|
||||
for (i = 0; i < data->t19_num_keys; i++) {
|
||||
if (data->t19_keymap[i] == KEY_RESERVED)
|
||||
continue;
|
||||
|
||||
/* Active-low switch */
|
||||
input_report_key(input, pdata->t19_keymap[i],
|
||||
input_report_key(input, data->t19_keymap[i],
|
||||
!(message[1] & BIT(i)));
|
||||
}
|
||||
}
|
||||
@@ -760,7 +771,7 @@ static void mxt_input_button(struct mxt_data *data, u8 *message)
|
||||
static void mxt_input_sync(struct mxt_data *data)
|
||||
{
|
||||
input_mt_report_pointer_emulation(data->input_dev,
|
||||
data->pdata->t19_num_keys);
|
||||
data->t19_num_keys);
|
||||
input_sync(data->input_dev);
|
||||
}
|
||||
|
||||
@@ -1199,7 +1210,7 @@ static int mxt_soft_reset(struct mxt_data *data)
|
||||
return ret;
|
||||
|
||||
/* Ignore CHG line for 100ms after reset */
|
||||
msleep(100);
|
||||
msleep(MXT_RESET_INVALID_CHG);
|
||||
|
||||
mxt_acquire_irq(data);
|
||||
|
||||
@@ -1909,7 +1920,6 @@ static void mxt_input_close(struct input_dev *dev);
|
||||
static void mxt_set_up_as_touchpad(struct input_dev *input_dev,
|
||||
struct mxt_data *data)
|
||||
{
|
||||
const struct mxt_platform_data *pdata = data->pdata;
|
||||
int i;
|
||||
|
||||
input_dev->name = "Atmel maXTouch Touchpad";
|
||||
@@ -1923,15 +1933,14 @@ static void mxt_set_up_as_touchpad(struct input_dev *input_dev,
|
||||
input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
|
||||
MXT_PIXELS_PER_MM);
|
||||
|
||||
for (i = 0; i < pdata->t19_num_keys; i++)
|
||||
if (pdata->t19_keymap[i] != KEY_RESERVED)
|
||||
for (i = 0; i < data->t19_num_keys; i++)
|
||||
if (data->t19_keymap[i] != KEY_RESERVED)
|
||||
input_set_capability(input_dev, EV_KEY,
|
||||
pdata->t19_keymap[i]);
|
||||
data->t19_keymap[i]);
|
||||
}
|
||||
|
||||
static int mxt_initialize_input_device(struct mxt_data *data)
|
||||
{
|
||||
const struct mxt_platform_data *pdata = data->pdata;
|
||||
struct device *dev = &data->client->dev;
|
||||
struct input_dev *input_dev;
|
||||
int error;
|
||||
@@ -1997,7 +2006,7 @@ static int mxt_initialize_input_device(struct mxt_data *data)
|
||||
}
|
||||
|
||||
/* If device has buttons we assume it is a touchpad */
|
||||
if (pdata->t19_num_keys) {
|
||||
if (data->t19_num_keys) {
|
||||
mxt_set_up_as_touchpad(input_dev, data);
|
||||
mt_flags |= INPUT_MT_POINTER;
|
||||
} else {
|
||||
@@ -2902,7 +2911,7 @@ static const struct attribute_group mxt_attr_group = {
|
||||
|
||||
static void mxt_start(struct mxt_data *data)
|
||||
{
|
||||
switch (data->pdata->suspend_mode) {
|
||||
switch (data->suspend_mode) {
|
||||
case MXT_SUSPEND_T9_CTRL:
|
||||
mxt_soft_reset(data);
|
||||
|
||||
@@ -2920,12 +2929,11 @@ static void mxt_start(struct mxt_data *data)
|
||||
mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void mxt_stop(struct mxt_data *data)
|
||||
{
|
||||
switch (data->pdata->suspend_mode) {
|
||||
switch (data->suspend_mode) {
|
||||
case MXT_SUSPEND_T9_CTRL:
|
||||
/* Touch disable */
|
||||
mxt_write_object(data,
|
||||
@@ -2955,140 +2963,75 @@ static void mxt_input_close(struct input_dev *dev)
|
||||
mxt_stop(data);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
|
||||
static int mxt_parse_device_properties(struct mxt_data *data)
|
||||
{
|
||||
struct mxt_platform_data *pdata;
|
||||
struct device_node *np = client->dev.of_node;
|
||||
static const char keymap_property[] = "linux,gpio-keymap";
|
||||
struct device *dev = &data->client->dev;
|
||||
u32 *keymap;
|
||||
int proplen, ret;
|
||||
int n_keys;
|
||||
int error;
|
||||
|
||||
if (!np)
|
||||
return ERR_PTR(-ENOENT);
|
||||
if (device_property_present(dev, keymap_property)) {
|
||||
n_keys = device_property_read_u32_array(dev, keymap_property,
|
||||
NULL, 0);
|
||||
if (n_keys <= 0) {
|
||||
error = n_keys < 0 ? n_keys : -EINVAL;
|
||||
dev_err(dev, "invalid/malformed '%s' property: %d\n",
|
||||
keymap_property, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (of_find_property(np, "linux,gpio-keymap", &proplen)) {
|
||||
pdata->t19_num_keys = proplen / sizeof(u32);
|
||||
|
||||
keymap = devm_kzalloc(&client->dev,
|
||||
pdata->t19_num_keys * sizeof(keymap[0]),
|
||||
GFP_KERNEL);
|
||||
keymap = devm_kmalloc_array(dev, n_keys, sizeof(*keymap),
|
||||
GFP_KERNEL);
|
||||
if (!keymap)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
return -ENOMEM;
|
||||
|
||||
ret = of_property_read_u32_array(np, "linux,gpio-keymap",
|
||||
keymap, pdata->t19_num_keys);
|
||||
if (ret)
|
||||
dev_warn(&client->dev,
|
||||
"Couldn't read linux,gpio-keymap: %d\n", ret);
|
||||
error = device_property_read_u32_array(dev, keymap_property,
|
||||
keymap, n_keys);
|
||||
if (error) {
|
||||
dev_err(dev, "failed to parse '%s' property: %d\n",
|
||||
keymap_property, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
pdata->t19_keymap = keymap;
|
||||
data->t19_keymap = keymap;
|
||||
data->t19_num_keys = n_keys;
|
||||
}
|
||||
|
||||
pdata->suspend_mode = MXT_SUSPEND_DEEP_SLEEP;
|
||||
|
||||
return pdata;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
|
||||
{
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
struct mxt_acpi_platform_data {
|
||||
const char *hid;
|
||||
struct mxt_platform_data pdata;
|
||||
};
|
||||
|
||||
static unsigned int samus_touchpad_buttons[] = {
|
||||
KEY_RESERVED,
|
||||
KEY_RESERVED,
|
||||
KEY_RESERVED,
|
||||
BTN_LEFT
|
||||
};
|
||||
|
||||
static struct mxt_acpi_platform_data samus_platform_data[] = {
|
||||
static const struct dmi_system_id chromebook_T9_suspend_dmi[] = {
|
||||
{
|
||||
/* Touchpad */
|
||||
.hid = "ATML0000",
|
||||
.pdata = {
|
||||
.t19_num_keys = ARRAY_SIZE(samus_touchpad_buttons),
|
||||
.t19_keymap = samus_touchpad_buttons,
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Touchscreen */
|
||||
.hid = "ATML0001",
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static unsigned int chromebook_tp_buttons[] = {
|
||||
KEY_RESERVED,
|
||||
KEY_RESERVED,
|
||||
KEY_RESERVED,
|
||||
KEY_RESERVED,
|
||||
KEY_RESERVED,
|
||||
BTN_LEFT
|
||||
};
|
||||
|
||||
static struct mxt_acpi_platform_data chromebook_platform_data[] = {
|
||||
{
|
||||
/* Touchpad */
|
||||
.hid = "ATML0000",
|
||||
.pdata = {
|
||||
.t19_num_keys = ARRAY_SIZE(chromebook_tp_buttons),
|
||||
.t19_keymap = chromebook_tp_buttons,
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Touchscreen */
|
||||
.hid = "ATML0001",
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct dmi_system_id mxt_dmi_table[] = {
|
||||
{
|
||||
/* 2015 Google Pixel */
|
||||
.ident = "Chromebook Pixel 2",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Samus"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
|
||||
},
|
||||
.driver_data = samus_platform_data,
|
||||
},
|
||||
{
|
||||
/* Samsung Chromebook Pro */
|
||||
.ident = "Samsung Chromebook Pro",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Google"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Caroline"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"),
|
||||
},
|
||||
.driver_data = samus_platform_data,
|
||||
},
|
||||
{
|
||||
/* Other Google Chromebooks */
|
||||
.ident = "Chromebook",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
|
||||
},
|
||||
.driver_data = chromebook_platform_data,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct mxt_platform_data *mxt_parse_acpi(struct i2c_client *client)
|
||||
static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
{
|
||||
struct acpi_device *adev;
|
||||
const struct dmi_system_id *system_id;
|
||||
const struct mxt_acpi_platform_data *acpi_pdata;
|
||||
struct mxt_data *data;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Ignore devices that do not have device properties attached to
|
||||
* them, as we need help determining whether we are dealing with
|
||||
* touch screen or touchpad.
|
||||
*
|
||||
* So far on x86 the only users of Atmel touch controllers are
|
||||
* Chromebooks, and chromeos_laptop driver will ensure that
|
||||
* necessary properties are provided (if firmware does not do that).
|
||||
*/
|
||||
if (!device_property_present(&client->dev, "compatible"))
|
||||
return -ENXIO;
|
||||
|
||||
/*
|
||||
* Ignore ACPI devices representing bootloader mode.
|
||||
@@ -3100,67 +3043,8 @@ static const struct mxt_platform_data *mxt_parse_acpi(struct i2c_client *client)
|
||||
* application mode addresses were all above 0x40, so we'll use it
|
||||
* as a threshold.
|
||||
*/
|
||||
if (client->addr < 0x40)
|
||||
return ERR_PTR(-ENXIO);
|
||||
|
||||
adev = ACPI_COMPANION(&client->dev);
|
||||
if (!adev)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
system_id = dmi_first_match(mxt_dmi_table);
|
||||
if (!system_id)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
acpi_pdata = system_id->driver_data;
|
||||
if (!acpi_pdata)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
while (acpi_pdata->hid) {
|
||||
if (!strcmp(acpi_device_hid(adev), acpi_pdata->hid))
|
||||
return &acpi_pdata->pdata;
|
||||
|
||||
acpi_pdata++;
|
||||
}
|
||||
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
#else
|
||||
static const struct mxt_platform_data *mxt_parse_acpi(struct i2c_client *client)
|
||||
{
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct mxt_platform_data *
|
||||
mxt_get_platform_data(struct i2c_client *client)
|
||||
{
|
||||
const struct mxt_platform_data *pdata;
|
||||
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
if (pdata)
|
||||
return pdata;
|
||||
|
||||
pdata = mxt_parse_dt(client);
|
||||
if (!IS_ERR(pdata) || PTR_ERR(pdata) != -ENOENT)
|
||||
return pdata;
|
||||
|
||||
pdata = mxt_parse_acpi(client);
|
||||
if (!IS_ERR(pdata) || PTR_ERR(pdata) != -ENOENT)
|
||||
return pdata;
|
||||
|
||||
dev_err(&client->dev, "No platform data specified\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
{
|
||||
struct mxt_data *data;
|
||||
const struct mxt_platform_data *pdata;
|
||||
int error;
|
||||
|
||||
pdata = mxt_get_platform_data(client);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
if (ACPI_COMPANION(&client->dev) && client->addr < 0x40)
|
||||
return -ENXIO;
|
||||
|
||||
data = devm_kzalloc(&client->dev, sizeof(struct mxt_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
@@ -3170,7 +3054,6 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
client->adapter->nr, client->addr);
|
||||
|
||||
data->client = client;
|
||||
data->pdata = pdata;
|
||||
data->irq = client->irq;
|
||||
i2c_set_clientdata(client, data);
|
||||
|
||||
@@ -3178,6 +3061,13 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
init_completion(&data->reset_completion);
|
||||
init_completion(&data->crc_completion);
|
||||
|
||||
data->suspend_mode = dmi_check_system(chromebook_T9_suspend_dmi) ?
|
||||
MXT_SUSPEND_T9_CTRL : MXT_SUSPEND_DEEP_SLEEP;
|
||||
|
||||
error = mxt_parse_device_properties(data);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
data->reset_gpio = devm_gpiod_get_optional(&client->dev,
|
||||
"reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(data->reset_gpio)) {
|
||||
@@ -3187,28 +3077,21 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
}
|
||||
|
||||
error = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, mxt_interrupt,
|
||||
pdata->irqflags | IRQF_ONESHOT,
|
||||
NULL, mxt_interrupt, IRQF_ONESHOT,
|
||||
client->name, data);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Failed to register interrupt\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
if (data->reset_gpio) {
|
||||
data->in_bootloader = true;
|
||||
msleep(MXT_RESET_TIME);
|
||||
reinit_completion(&data->bl_completion);
|
||||
gpiod_set_value(data->reset_gpio, 1);
|
||||
error = mxt_wait_for_completion(data, &data->bl_completion,
|
||||
MXT_RESET_TIMEOUT);
|
||||
if (error)
|
||||
return error;
|
||||
data->in_bootloader = false;
|
||||
}
|
||||
|
||||
disable_irq(client->irq);
|
||||
|
||||
if (data->reset_gpio) {
|
||||
msleep(MXT_RESET_GPIO_TIME);
|
||||
gpiod_set_value(data->reset_gpio, 1);
|
||||
msleep(MXT_RESET_INVALID_CHG);
|
||||
}
|
||||
|
||||
error = mxt_initialize(data);
|
||||
if (error)
|
||||
return error;
|
||||
@@ -3313,7 +3196,7 @@ MODULE_DEVICE_TABLE(i2c, mxt_id);
|
||||
static struct i2c_driver mxt_driver = {
|
||||
.driver = {
|
||||
.name = "atmel_mxt_ts",
|
||||
.of_match_table = of_match_ptr(mxt_of_match),
|
||||
.of_match_table = mxt_of_match,
|
||||
.acpi_match_table = ACPI_PTR(mxt_acpi_id),
|
||||
.pm = &mxt_pm_ops,
|
||||
},
|
||||
|
520
drivers/input/touchscreen/chipone_icn8505.c
Normal file
520
drivers/input/touchscreen/chipone_icn8505.c
Normal file
@@ -0,0 +1,520 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Driver for ChipOne icn8505 i2c touchscreen controller
|
||||
*
|
||||
* Copyright (c) 2015-2018 Red Hat Inc.
|
||||
*
|
||||
* Red Hat authors:
|
||||
* Hans de Goede <hdegoede@redhat.com>
|
||||
*/
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/input/touchscreen.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
/* Normal operation mode defines */
|
||||
#define ICN8505_REG_ADDR_WIDTH 16
|
||||
|
||||
#define ICN8505_REG_POWER 0x0004
|
||||
#define ICN8505_REG_TOUCHDATA 0x1000
|
||||
#define ICN8505_REG_CONFIGDATA 0x8000
|
||||
|
||||
/* ICN8505_REG_POWER commands */
|
||||
#define ICN8505_POWER_ACTIVE 0x00
|
||||
#define ICN8505_POWER_MONITOR 0x01
|
||||
#define ICN8505_POWER_HIBERNATE 0x02
|
||||
/*
|
||||
* The Android driver uses these to turn on/off the charger filter, but the
|
||||
* filter is way too aggressive making e.g. onscreen keyboards unusable.
|
||||
*/
|
||||
#define ICN8505_POWER_ENA_CHARGER_MODE 0x55
|
||||
#define ICN8505_POWER_DIS_CHARGER_MODE 0x66
|
||||
|
||||
#define ICN8505_MAX_TOUCHES 10
|
||||
|
||||
/* Programming mode defines */
|
||||
#define ICN8505_PROG_I2C_ADDR 0x30
|
||||
#define ICN8505_PROG_REG_ADDR_WIDTH 24
|
||||
|
||||
#define MAX_FW_UPLOAD_TRIES 3
|
||||
|
||||
struct icn8505_touch {
|
||||
u8 slot;
|
||||
u8 x[2];
|
||||
u8 y[2];
|
||||
u8 pressure; /* Seems more like finger width then pressure really */
|
||||
u8 event;
|
||||
/* The difference between 2 and 3 is unclear */
|
||||
#define ICN8505_EVENT_NO_DATA 1 /* No finger seen yet since wakeup */
|
||||
#define ICN8505_EVENT_UPDATE1 2 /* New or updated coordinates */
|
||||
#define ICN8505_EVENT_UPDATE2 3 /* New or updated coordinates */
|
||||
#define ICN8505_EVENT_END 4 /* Finger lifted */
|
||||
} __packed;
|
||||
|
||||
struct icn8505_touch_data {
|
||||
u8 softbutton;
|
||||
u8 touch_count;
|
||||
struct icn8505_touch touches[ICN8505_MAX_TOUCHES];
|
||||
} __packed;
|
||||
|
||||
struct icn8505_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
struct gpio_desc *wake_gpio;
|
||||
struct touchscreen_properties prop;
|
||||
char firmware_name[32];
|
||||
};
|
||||
|
||||
static int icn8505_read_xfer(struct i2c_client *client, u16 i2c_addr,
|
||||
int reg_addr, int reg_addr_width,
|
||||
void *data, int len, bool silent)
|
||||
{
|
||||
u8 buf[3];
|
||||
int i, ret;
|
||||
struct i2c_msg msg[2] = {
|
||||
{
|
||||
.addr = i2c_addr,
|
||||
.buf = buf,
|
||||
.len = reg_addr_width / 8,
|
||||
},
|
||||
{
|
||||
.addr = i2c_addr,
|
||||
.flags = I2C_M_RD,
|
||||
.buf = data,
|
||||
.len = len,
|
||||
}
|
||||
};
|
||||
|
||||
for (i = 0; i < (reg_addr_width / 8); i++)
|
||||
buf[i] = (reg_addr >> (reg_addr_width - (i + 1) * 8)) & 0xff;
|
||||
|
||||
ret = i2c_transfer(client->adapter, msg, 2);
|
||||
if (ret != ARRAY_SIZE(msg)) {
|
||||
if (ret >= 0)
|
||||
ret = -EIO;
|
||||
if (!silent)
|
||||
dev_err(&client->dev,
|
||||
"Error reading addr %#x reg %#x: %d\n",
|
||||
i2c_addr, reg_addr, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icn8505_write_xfer(struct i2c_client *client, u16 i2c_addr,
|
||||
int reg_addr, int reg_addr_width,
|
||||
const void *data, int len, bool silent)
|
||||
{
|
||||
u8 buf[3 + 32]; /* 3 bytes for 24 bit reg-addr + 32 bytes max len */
|
||||
int i, ret;
|
||||
struct i2c_msg msg = {
|
||||
.addr = i2c_addr,
|
||||
.buf = buf,
|
||||
.len = reg_addr_width / 8 + len,
|
||||
};
|
||||
|
||||
if (WARN_ON(len > 32))
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < (reg_addr_width / 8); i++)
|
||||
buf[i] = (reg_addr >> (reg_addr_width - (i + 1) * 8)) & 0xff;
|
||||
|
||||
memcpy(buf + reg_addr_width / 8, data, len);
|
||||
|
||||
ret = i2c_transfer(client->adapter, &msg, 1);
|
||||
if (ret != 1) {
|
||||
if (ret >= 0)
|
||||
ret = -EIO;
|
||||
if (!silent)
|
||||
dev_err(&client->dev,
|
||||
"Error writing addr %#x reg %#x: %d\n",
|
||||
i2c_addr, reg_addr, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icn8505_read_data(struct icn8505_data *icn8505, int reg,
|
||||
void *buf, int len)
|
||||
{
|
||||
return icn8505_read_xfer(icn8505->client, icn8505->client->addr, reg,
|
||||
ICN8505_REG_ADDR_WIDTH, buf, len, false);
|
||||
}
|
||||
|
||||
static int icn8505_read_reg_silent(struct icn8505_data *icn8505, int reg)
|
||||
{
|
||||
u8 buf;
|
||||
int error;
|
||||
|
||||
error = icn8505_read_xfer(icn8505->client, icn8505->client->addr, reg,
|
||||
ICN8505_REG_ADDR_WIDTH, &buf, 1, true);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int icn8505_write_reg(struct icn8505_data *icn8505, int reg, u8 val)
|
||||
{
|
||||
return icn8505_write_xfer(icn8505->client, icn8505->client->addr, reg,
|
||||
ICN8505_REG_ADDR_WIDTH, &val, 1, false);
|
||||
}
|
||||
|
||||
static int icn8505_read_prog_data(struct icn8505_data *icn8505, int reg,
|
||||
void *buf, int len)
|
||||
{
|
||||
return icn8505_read_xfer(icn8505->client, ICN8505_PROG_I2C_ADDR, reg,
|
||||
ICN8505_PROG_REG_ADDR_WIDTH, buf, len, false);
|
||||
}
|
||||
|
||||
static int icn8505_write_prog_data(struct icn8505_data *icn8505, int reg,
|
||||
const void *buf, int len)
|
||||
{
|
||||
return icn8505_write_xfer(icn8505->client, ICN8505_PROG_I2C_ADDR, reg,
|
||||
ICN8505_PROG_REG_ADDR_WIDTH, buf, len, false);
|
||||
}
|
||||
|
||||
static int icn8505_write_prog_reg(struct icn8505_data *icn8505, int reg, u8 val)
|
||||
{
|
||||
return icn8505_write_xfer(icn8505->client, ICN8505_PROG_I2C_ADDR, reg,
|
||||
ICN8505_PROG_REG_ADDR_WIDTH, &val, 1, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note this function uses a number of magic register addresses and values,
|
||||
* there are deliberately no defines for these because the algorithm is taken
|
||||
* from the icn85xx Android driver and I do not want to make up possibly wrong
|
||||
* names for the addresses and/or values.
|
||||
*/
|
||||
static int icn8505_try_fw_upload(struct icn8505_data *icn8505,
|
||||
const struct firmware *fw)
|
||||
{
|
||||
struct device *dev = &icn8505->client->dev;
|
||||
size_t offset, count;
|
||||
int error;
|
||||
u8 buf[4];
|
||||
u32 crc;
|
||||
|
||||
/* Put the controller in programming mode */
|
||||
error = icn8505_write_prog_reg(icn8505, 0xcc3355, 0x5a);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
usleep_range(2000, 5000);
|
||||
|
||||
error = icn8505_write_prog_reg(icn8505, 0x040400, 0x01);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
usleep_range(2000, 5000);
|
||||
|
||||
error = icn8505_read_prog_data(icn8505, 0x040002, buf, 1);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (buf[0] != 0x85) {
|
||||
dev_err(dev, "Failed to enter programming mode\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
usleep_range(1000, 5000);
|
||||
|
||||
/* Enable CRC mode */
|
||||
error = icn8505_write_prog_reg(icn8505, 0x40028, 1);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Send the firmware to SRAM */
|
||||
for (offset = 0; offset < fw->size; offset += count) {
|
||||
count = min_t(size_t, fw->size - offset, 32);
|
||||
error = icn8505_write_prog_data(icn8505, offset,
|
||||
fw->data + offset, count);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Disable CRC mode */
|
||||
error = icn8505_write_prog_reg(icn8505, 0x40028, 0);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Get and check length and CRC */
|
||||
error = icn8505_read_prog_data(icn8505, 0x40034, buf, 2);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (get_unaligned_le16(buf) != fw->size) {
|
||||
dev_warn(dev, "Length mismatch after uploading fw\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
error = icn8505_read_prog_data(icn8505, 0x4002c, buf, 4);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
crc = crc32_be(0, fw->data, fw->size);
|
||||
if (get_unaligned_le32(buf) != crc) {
|
||||
dev_warn(dev, "CRC mismatch after uploading fw\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Boot controller from SRAM */
|
||||
error = icn8505_write_prog_reg(icn8505, 0x40400, 0x03);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
usleep_range(2000, 5000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icn8505_upload_fw(struct icn8505_data *icn8505)
|
||||
{
|
||||
struct device *dev = &icn8505->client->dev;
|
||||
const struct firmware *fw;
|
||||
int i, error;
|
||||
|
||||
/*
|
||||
* Always load the firmware, even if we don't need it at boot, we
|
||||
* we may need it at resume. Having loaded it once will make the
|
||||
* firmware class code cache it at suspend/resume.
|
||||
*/
|
||||
error = request_firmware(&fw, icn8505->firmware_name, dev);
|
||||
if (error) {
|
||||
dev_err(dev, "Firmware request error %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Check if the controller is not already up and running */
|
||||
if (icn8505_read_reg_silent(icn8505, 0x000a) == 0x85)
|
||||
goto success;
|
||||
|
||||
for (i = 1; i <= MAX_FW_UPLOAD_TRIES; i++) {
|
||||
error = icn8505_try_fw_upload(icn8505, fw);
|
||||
if (!error)
|
||||
goto success;
|
||||
|
||||
dev_err(dev, "Failed to upload firmware: %d (attempt %d/%d)\n",
|
||||
error, i, MAX_FW_UPLOAD_TRIES);
|
||||
usleep_range(2000, 5000);
|
||||
}
|
||||
|
||||
success:
|
||||
release_firmware(fw);
|
||||
return error;
|
||||
}
|
||||
|
||||
static bool icn8505_touch_active(u8 event)
|
||||
{
|
||||
return event == ICN8505_EVENT_UPDATE1 ||
|
||||
event == ICN8505_EVENT_UPDATE2;
|
||||
}
|
||||
|
||||
static irqreturn_t icn8505_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct icn8505_data *icn8505 = dev_id;
|
||||
struct device *dev = &icn8505->client->dev;
|
||||
struct icn8505_touch_data touch_data;
|
||||
int i, error;
|
||||
|
||||
error = icn8505_read_data(icn8505, ICN8505_REG_TOUCHDATA,
|
||||
&touch_data, sizeof(touch_data));
|
||||
if (error) {
|
||||
dev_err(dev, "Error reading touch data: %d\n", error);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (touch_data.touch_count > ICN8505_MAX_TOUCHES) {
|
||||
dev_warn(dev, "Too many touches %d > %d\n",
|
||||
touch_data.touch_count, ICN8505_MAX_TOUCHES);
|
||||
touch_data.touch_count = ICN8505_MAX_TOUCHES;
|
||||
}
|
||||
|
||||
for (i = 0; i < touch_data.touch_count; i++) {
|
||||
struct icn8505_touch *touch = &touch_data.touches[i];
|
||||
bool act = icn8505_touch_active(touch->event);
|
||||
|
||||
input_mt_slot(icn8505->input, touch->slot);
|
||||
input_mt_report_slot_state(icn8505->input, MT_TOOL_FINGER, act);
|
||||
if (!act)
|
||||
continue;
|
||||
|
||||
touchscreen_report_pos(icn8505->input, &icn8505->prop,
|
||||
get_unaligned_le16(touch->x),
|
||||
get_unaligned_le16(touch->y),
|
||||
true);
|
||||
}
|
||||
|
||||
input_mt_sync_frame(icn8505->input);
|
||||
input_report_key(icn8505->input, KEY_LEFTMETA,
|
||||
touch_data.softbutton == 1);
|
||||
input_sync(icn8505->input);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int icn8505_probe_acpi(struct icn8505_data *icn8505, struct device *dev)
|
||||
{
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
const char *subsys = "unknown";
|
||||
struct acpi_device *adev;
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
|
||||
adev = ACPI_COMPANION(dev);
|
||||
if (!adev)
|
||||
return -ENODEV;
|
||||
|
||||
status = acpi_evaluate_object(adev->handle, "_SUB", NULL, &buffer);
|
||||
if (ACPI_SUCCESS(status)) {
|
||||
obj = buffer.pointer;
|
||||
if (obj->type == ACPI_TYPE_STRING)
|
||||
subsys = obj->string.pointer;
|
||||
else
|
||||
dev_warn(dev, "Warning ACPI _SUB did not return a string\n");
|
||||
} else {
|
||||
dev_warn(dev, "Warning ACPI _SUB failed: %#x\n", status);
|
||||
buffer.pointer = NULL;
|
||||
}
|
||||
|
||||
snprintf(icn8505->firmware_name, sizeof(icn8505->firmware_name),
|
||||
"chipone/icn8505-%s.fw", subsys);
|
||||
|
||||
kfree(buffer.pointer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icn8505_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct icn8505_data *icn8505;
|
||||
struct input_dev *input;
|
||||
__le16 resolution[2];
|
||||
int error;
|
||||
|
||||
if (!client->irq) {
|
||||
dev_err(dev, "No irq specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
icn8505 = devm_kzalloc(dev, sizeof(*icn8505), GFP_KERNEL);
|
||||
if (!icn8505)
|
||||
return -ENOMEM;
|
||||
|
||||
input = devm_input_allocate_device(dev);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
input->name = client->name;
|
||||
input->id.bustype = BUS_I2C;
|
||||
|
||||
input_set_capability(input, EV_ABS, ABS_MT_POSITION_X);
|
||||
input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y);
|
||||
input_set_capability(input, EV_KEY, KEY_LEFTMETA);
|
||||
|
||||
icn8505->client = client;
|
||||
icn8505->input = input;
|
||||
input_set_drvdata(input, icn8505);
|
||||
|
||||
error = icn8505_probe_acpi(icn8505, dev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = icn8505_upload_fw(icn8505);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = icn8505_read_data(icn8505, ICN8505_REG_CONFIGDATA,
|
||||
resolution, sizeof(resolution));
|
||||
if (error) {
|
||||
dev_err(dev, "Error reading resolution: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0,
|
||||
le16_to_cpu(resolution[0]) - 1, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
|
||||
le16_to_cpu(resolution[1]) - 1, 0, 0);
|
||||
|
||||
touchscreen_parse_properties(input, true, &icn8505->prop);
|
||||
if (!input_abs_get_max(input, ABS_MT_POSITION_X) ||
|
||||
!input_abs_get_max(input, ABS_MT_POSITION_Y)) {
|
||||
dev_err(dev, "Error touchscreen-size-x and/or -y missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
error = input_mt_init_slots(input, ICN8505_MAX_TOUCHES,
|
||||
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = devm_request_threaded_irq(dev, client->irq, NULL, icn8505_irq,
|
||||
IRQF_ONESHOT, client->name, icn8505);
|
||||
if (error) {
|
||||
dev_err(dev, "Error requesting irq: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
i2c_set_clientdata(client, icn8505);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused icn8505_suspend(struct device *dev)
|
||||
{
|
||||
struct icn8505_data *icn8505 = i2c_get_clientdata(to_i2c_client(dev));
|
||||
|
||||
disable_irq(icn8505->client->irq);
|
||||
|
||||
icn8505_write_reg(icn8505, ICN8505_REG_POWER, ICN8505_POWER_HIBERNATE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused icn8505_resume(struct device *dev)
|
||||
{
|
||||
struct icn8505_data *icn8505 = i2c_get_clientdata(to_i2c_client(dev));
|
||||
int error;
|
||||
|
||||
error = icn8505_upload_fw(icn8505);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
enable_irq(icn8505->client->irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(icn8505_pm_ops, icn8505_suspend, icn8505_resume);
|
||||
|
||||
static const struct acpi_device_id icn8505_acpi_match[] = {
|
||||
{ "CHPN0001" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, icn8505_acpi_match);
|
||||
|
||||
static struct i2c_driver icn8505_driver = {
|
||||
.driver = {
|
||||
.name = "chipone_icn8505",
|
||||
.pm = &icn8505_pm_ops,
|
||||
.acpi_match_table = icn8505_acpi_match,
|
||||
},
|
||||
.probe_new = icn8505_probe,
|
||||
};
|
||||
|
||||
module_i2c_driver(icn8505_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ChipOne icn8505 I2C Touchscreen Driver");
|
||||
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
|
||||
MODULE_LICENSE("GPL");
|
@@ -933,6 +933,7 @@ MODULE_DEVICE_TABLE(i2c, goodix_ts_id);
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id goodix_acpi_match[] = {
|
||||
{ "GDIX1001", 0 },
|
||||
{ "GDIX1002", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);
|
||||
|
@@ -17,7 +17,7 @@
|
||||
* found in Gateway AOL Connected Touchpad computers.
|
||||
*
|
||||
* Documentation for ICS MK712 can be found at:
|
||||
* http://www.idt.com/products/getDoc.cfm?docID=18713923
|
||||
* https://www.idt.com/general-parts/mk712-touch-screen-controller
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@@ -38,6 +38,7 @@
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-dev.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/videobuf2-v4l2.h>
|
||||
#include <media/videobuf2-dma-sg.h>
|
||||
|
||||
@@ -81,7 +82,10 @@ struct sur40_blob {
|
||||
|
||||
__le32 area; /* size in pixels/pressure (?) */
|
||||
|
||||
u8 padding[32];
|
||||
u8 padding[24];
|
||||
|
||||
__le32 tag_id; /* valid when type == 0x04 (SUR40_TAG) */
|
||||
__le32 unknown;
|
||||
|
||||
} __packed;
|
||||
|
||||
@@ -146,6 +150,40 @@ struct sur40_image_header {
|
||||
#define SUR40_TOUCH 0x02
|
||||
#define SUR40_TAG 0x04
|
||||
|
||||
/* video controls */
|
||||
#define SUR40_BRIGHTNESS_MAX 0xff
|
||||
#define SUR40_BRIGHTNESS_MIN 0x00
|
||||
#define SUR40_BRIGHTNESS_DEF 0xff
|
||||
|
||||
#define SUR40_CONTRAST_MAX 0x0f
|
||||
#define SUR40_CONTRAST_MIN 0x00
|
||||
#define SUR40_CONTRAST_DEF 0x0a
|
||||
|
||||
#define SUR40_GAIN_MAX 0x09
|
||||
#define SUR40_GAIN_MIN 0x00
|
||||
#define SUR40_GAIN_DEF 0x08
|
||||
|
||||
#define SUR40_BACKLIGHT_MAX 0x01
|
||||
#define SUR40_BACKLIGHT_MIN 0x00
|
||||
#define SUR40_BACKLIGHT_DEF 0x01
|
||||
|
||||
#define sur40_str(s) #s
|
||||
#define SUR40_PARAM_RANGE(lo, hi) " (range " sur40_str(lo) "-" sur40_str(hi) ")"
|
||||
|
||||
/* module parameters */
|
||||
static uint brightness = SUR40_BRIGHTNESS_DEF;
|
||||
module_param(brightness, uint, 0644);
|
||||
MODULE_PARM_DESC(brightness, "set initial brightness"
|
||||
SUR40_PARAM_RANGE(SUR40_BRIGHTNESS_MIN, SUR40_BRIGHTNESS_MAX));
|
||||
static uint contrast = SUR40_CONTRAST_DEF;
|
||||
module_param(contrast, uint, 0644);
|
||||
MODULE_PARM_DESC(contrast, "set initial contrast"
|
||||
SUR40_PARAM_RANGE(SUR40_CONTRAST_MIN, SUR40_CONTRAST_MAX));
|
||||
static uint gain = SUR40_GAIN_DEF;
|
||||
module_param(gain, uint, 0644);
|
||||
MODULE_PARM_DESC(gain, "set initial gain"
|
||||
SUR40_PARAM_RANGE(SUR40_GAIN_MIN, SUR40_GAIN_MAX));
|
||||
|
||||
static const struct v4l2_pix_format sur40_pix_format[] = {
|
||||
{
|
||||
.pixelformat = V4L2_TCH_FMT_TU08,
|
||||
@@ -178,6 +216,7 @@ struct sur40_state {
|
||||
struct video_device vdev;
|
||||
struct mutex lock;
|
||||
struct v4l2_pix_format pix_fmt;
|
||||
struct v4l2_ctrl_handler hdl;
|
||||
|
||||
struct vb2_queue queue;
|
||||
struct list_head buf_list;
|
||||
@@ -187,6 +226,7 @@ struct sur40_state {
|
||||
struct sur40_data *bulk_in_buffer;
|
||||
size_t bulk_in_size;
|
||||
u8 bulk_in_epaddr;
|
||||
u8 vsvideo;
|
||||
|
||||
char phys[64];
|
||||
};
|
||||
@@ -200,6 +240,11 @@ struct sur40_buffer {
|
||||
static const struct video_device sur40_video_device;
|
||||
static const struct vb2_queue sur40_queue;
|
||||
static void sur40_process_video(struct sur40_state *sur40);
|
||||
static int sur40_s_ctrl(struct v4l2_ctrl *ctrl);
|
||||
|
||||
static const struct v4l2_ctrl_ops sur40_ctrl_ops = {
|
||||
.s_ctrl = sur40_s_ctrl,
|
||||
};
|
||||
|
||||
/*
|
||||
* Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT
|
||||
@@ -220,6 +265,81 @@ static int sur40_command(struct sur40_state *dev,
|
||||
0x00, index, buffer, size, 1000);
|
||||
}
|
||||
|
||||
/* poke a byte in the panel register space */
|
||||
static int sur40_poke(struct sur40_state *dev, u8 offset, u8 value)
|
||||
{
|
||||
int result;
|
||||
u8 index = 0x96; // 0xae for permanent write
|
||||
|
||||
result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0),
|
||||
SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
|
||||
0x32, index, NULL, 0, 1000);
|
||||
if (result < 0)
|
||||
goto error;
|
||||
msleep(5);
|
||||
|
||||
result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0),
|
||||
SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
|
||||
0x72, offset, NULL, 0, 1000);
|
||||
if (result < 0)
|
||||
goto error;
|
||||
msleep(5);
|
||||
|
||||
result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0),
|
||||
SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
|
||||
0xb2, value, NULL, 0, 1000);
|
||||
if (result < 0)
|
||||
goto error;
|
||||
msleep(5);
|
||||
|
||||
error:
|
||||
return result;
|
||||
}
|
||||
|
||||
static int sur40_set_preprocessor(struct sur40_state *dev, u8 value)
|
||||
{
|
||||
u8 setting_07[2] = { 0x01, 0x00 };
|
||||
u8 setting_17[2] = { 0x85, 0x80 };
|
||||
int result;
|
||||
|
||||
if (value > 1)
|
||||
return -ERANGE;
|
||||
|
||||
result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0),
|
||||
SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
|
||||
0x07, setting_07[value], NULL, 0, 1000);
|
||||
if (result < 0)
|
||||
goto error;
|
||||
msleep(5);
|
||||
|
||||
result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0),
|
||||
SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
|
||||
0x17, setting_17[value], NULL, 0, 1000);
|
||||
if (result < 0)
|
||||
goto error;
|
||||
msleep(5);
|
||||
|
||||
error:
|
||||
return result;
|
||||
}
|
||||
|
||||
static void sur40_set_vsvideo(struct sur40_state *handle, u8 value)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
sur40_poke(handle, 0x1c+i, value);
|
||||
handle->vsvideo = value;
|
||||
}
|
||||
|
||||
static void sur40_set_irlevel(struct sur40_state *handle, u8 value)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
sur40_poke(handle, 0x08+(2*i), value);
|
||||
}
|
||||
|
||||
/* Initialization routine, called from sur40_open */
|
||||
static int sur40_init(struct sur40_state *dev)
|
||||
{
|
||||
@@ -631,6 +751,36 @@ static int sur40_probe(struct usb_interface *interface,
|
||||
sur40->vdev.queue = &sur40->queue;
|
||||
video_set_drvdata(&sur40->vdev, sur40);
|
||||
|
||||
/* initialize the control handler for 4 controls */
|
||||
v4l2_ctrl_handler_init(&sur40->hdl, 4);
|
||||
sur40->v4l2.ctrl_handler = &sur40->hdl;
|
||||
sur40->vsvideo = (SUR40_CONTRAST_DEF << 4) | SUR40_GAIN_DEF;
|
||||
|
||||
v4l2_ctrl_new_std(&sur40->hdl, &sur40_ctrl_ops, V4L2_CID_BRIGHTNESS,
|
||||
SUR40_BRIGHTNESS_MIN, SUR40_BRIGHTNESS_MAX, 1, clamp(brightness,
|
||||
(uint)SUR40_BRIGHTNESS_MIN, (uint)SUR40_BRIGHTNESS_MAX));
|
||||
|
||||
v4l2_ctrl_new_std(&sur40->hdl, &sur40_ctrl_ops, V4L2_CID_CONTRAST,
|
||||
SUR40_CONTRAST_MIN, SUR40_CONTRAST_MAX, 1, clamp(contrast,
|
||||
(uint)SUR40_CONTRAST_MIN, (uint)SUR40_CONTRAST_MAX));
|
||||
|
||||
v4l2_ctrl_new_std(&sur40->hdl, &sur40_ctrl_ops, V4L2_CID_GAIN,
|
||||
SUR40_GAIN_MIN, SUR40_GAIN_MAX, 1, clamp(gain,
|
||||
(uint)SUR40_GAIN_MIN, (uint)SUR40_GAIN_MAX));
|
||||
|
||||
v4l2_ctrl_new_std(&sur40->hdl, &sur40_ctrl_ops,
|
||||
V4L2_CID_BACKLIGHT_COMPENSATION, SUR40_BACKLIGHT_MIN,
|
||||
SUR40_BACKLIGHT_MAX, 1, SUR40_BACKLIGHT_DEF);
|
||||
|
||||
v4l2_ctrl_handler_setup(&sur40->hdl);
|
||||
|
||||
if (sur40->hdl.error) {
|
||||
dev_err(&interface->dev,
|
||||
"Unable to register video controls.");
|
||||
v4l2_ctrl_handler_free(&sur40->hdl);
|
||||
goto err_unreg_v4l2;
|
||||
}
|
||||
|
||||
error = video_register_device(&sur40->vdev, VFL_TYPE_TOUCH, -1);
|
||||
if (error) {
|
||||
dev_err(&interface->dev,
|
||||
@@ -663,6 +813,7 @@ static void sur40_disconnect(struct usb_interface *interface)
|
||||
{
|
||||
struct sur40_state *sur40 = usb_get_intfdata(interface);
|
||||
|
||||
v4l2_ctrl_handler_free(&sur40->hdl);
|
||||
video_unregister_device(&sur40->vdev);
|
||||
v4l2_device_unregister(&sur40->v4l2);
|
||||
|
||||
@@ -856,6 +1007,31 @@ static int sur40_vidioc_g_fmt(struct file *file, void *priv,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sur40_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct sur40_state *sur40 = container_of(ctrl->handler,
|
||||
struct sur40_state, hdl);
|
||||
u8 value = sur40->vsvideo;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
sur40_set_irlevel(sur40, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_CONTRAST:
|
||||
value = (value & 0x0f) | (ctrl->val << 4);
|
||||
sur40_set_vsvideo(sur40, value);
|
||||
break;
|
||||
case V4L2_CID_GAIN:
|
||||
value = (value & 0xf0) | (ctrl->val);
|
||||
sur40_set_vsvideo(sur40, value);
|
||||
break;
|
||||
case V4L2_CID_BACKLIGHT_COMPENSATION:
|
||||
sur40_set_preprocessor(sur40, ctrl->val);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sur40_ioctl_parm(struct file *file, void *priv,
|
||||
struct v4l2_streamparm *p)
|
||||
{
|
||||
|
@@ -34,6 +34,8 @@
|
||||
#define SEQ_SETTLE 275
|
||||
#define MAX_12BIT ((1 << 12) - 1)
|
||||
|
||||
#define TSC_IRQENB_MASK (IRQENB_FIFO0THRES | IRQENB_EOS | IRQENB_HW_PEN)
|
||||
|
||||
static const int config_pins[] = {
|
||||
STEPCONFIG_XPP,
|
||||
STEPCONFIG_XNN,
|
||||
@@ -274,6 +276,7 @@ static irqreturn_t titsc_irq(int irq, void *dev)
|
||||
if (status & IRQENB_HW_PEN) {
|
||||
ts_dev->pen_down = true;
|
||||
irqclr |= IRQENB_HW_PEN;
|
||||
pm_stay_awake(ts_dev->mfd_tscadc->dev);
|
||||
}
|
||||
|
||||
if (status & IRQENB_PENUP) {
|
||||
@@ -283,6 +286,7 @@ static irqreturn_t titsc_irq(int irq, void *dev)
|
||||
input_report_key(input_dev, BTN_TOUCH, 0);
|
||||
input_report_abs(input_dev, ABS_PRESSURE, 0);
|
||||
input_sync(input_dev);
|
||||
pm_relax(ts_dev->mfd_tscadc->dev);
|
||||
} else {
|
||||
ts_dev->pen_down = true;
|
||||
}
|
||||
@@ -432,6 +436,7 @@ static int titsc_probe(struct platform_device *pdev)
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
titsc_writel(ts_dev, REG_IRQSTATUS, TSC_IRQENB_MASK);
|
||||
titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES);
|
||||
titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_EOS);
|
||||
err = titsc_config_wires(ts_dev);
|
||||
@@ -495,6 +500,7 @@ static int __maybe_unused titsc_suspend(struct device *dev)
|
||||
|
||||
tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev));
|
||||
if (device_may_wakeup(tscadc_dev->dev)) {
|
||||
titsc_writel(ts_dev, REG_IRQSTATUS, TSC_IRQENB_MASK);
|
||||
idle = titsc_readl(ts_dev, REG_IRQENABLE);
|
||||
titsc_writel(ts_dev, REG_IRQENABLE,
|
||||
(idle | IRQENB_HW_PEN));
|
||||
@@ -513,6 +519,7 @@ static int __maybe_unused titsc_resume(struct device *dev)
|
||||
titsc_writel(ts_dev, REG_IRQWAKEUP,
|
||||
0x00);
|
||||
titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
|
||||
pm_relax(ts_dev->mfd_tscadc->dev);
|
||||
}
|
||||
titsc_step_config(ts_dev);
|
||||
titsc_writel(ts_dev, REG_FIFO0THR,
|
||||
|
@@ -440,6 +440,8 @@ static int panjit_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
|
||||
#define MTOUCHUSB_RESET 7
|
||||
#define MTOUCHUSB_REQ_CTRLLR_ID 10
|
||||
|
||||
#define MTOUCHUSB_REQ_CTRLLR_ID_LEN 16
|
||||
|
||||
static int mtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
|
||||
{
|
||||
if (hwcalib_xy) {
|
||||
@@ -454,11 +456,93 @@ static int mtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct mtouch_priv {
|
||||
u8 fw_rev_major;
|
||||
u8 fw_rev_minor;
|
||||
};
|
||||
|
||||
static ssize_t mtouch_firmware_rev_show(struct device *dev,
|
||||
struct device_attribute *attr, char *output)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);
|
||||
struct mtouch_priv *priv = usbtouch->priv;
|
||||
|
||||
return scnprintf(output, PAGE_SIZE, "%1x.%1x\n",
|
||||
priv->fw_rev_major, priv->fw_rev_minor);
|
||||
}
|
||||
static DEVICE_ATTR(firmware_rev, 0444, mtouch_firmware_rev_show, NULL);
|
||||
|
||||
static struct attribute *mtouch_attrs[] = {
|
||||
&dev_attr_firmware_rev.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group mtouch_attr_group = {
|
||||
.attrs = mtouch_attrs,
|
||||
};
|
||||
|
||||
static int mtouch_get_fw_revision(struct usbtouch_usb *usbtouch)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(usbtouch->interface);
|
||||
struct mtouch_priv *priv = usbtouch->priv;
|
||||
u8 *buf;
|
||||
int ret;
|
||||
|
||||
buf = kzalloc(MTOUCHUSB_REQ_CTRLLR_ID_LEN, GFP_NOIO);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
|
||||
MTOUCHUSB_REQ_CTRLLR_ID,
|
||||
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
0, 0, buf, MTOUCHUSB_REQ_CTRLLR_ID_LEN,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
if (ret != MTOUCHUSB_REQ_CTRLLR_ID_LEN) {
|
||||
dev_warn(&usbtouch->interface->dev,
|
||||
"Failed to read FW rev: %d\n", ret);
|
||||
ret = ret < 0 ? ret : -EIO;
|
||||
goto free;
|
||||
}
|
||||
|
||||
priv->fw_rev_major = buf[3];
|
||||
priv->fw_rev_minor = buf[4];
|
||||
|
||||
ret = 0;
|
||||
|
||||
free:
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtouch_alloc(struct usbtouch_usb *usbtouch)
|
||||
{
|
||||
int ret;
|
||||
|
||||
usbtouch->priv = kmalloc(sizeof(struct mtouch_priv), GFP_KERNEL);
|
||||
if (!usbtouch->priv)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = sysfs_create_group(&usbtouch->interface->dev.kobj,
|
||||
&mtouch_attr_group);
|
||||
if (ret) {
|
||||
kfree(usbtouch->priv);
|
||||
usbtouch->priv = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtouch_init(struct usbtouch_usb *usbtouch)
|
||||
{
|
||||
int ret, i;
|
||||
struct usb_device *udev = interface_to_usbdev(usbtouch->interface);
|
||||
|
||||
ret = mtouch_get_fw_revision(usbtouch);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
|
||||
MTOUCHUSB_RESET,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
@@ -492,6 +576,14 @@ static int mtouch_init(struct usbtouch_usb *usbtouch)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtouch_exit(struct usbtouch_usb *usbtouch)
|
||||
{
|
||||
struct mtouch_priv *priv = usbtouch->priv;
|
||||
|
||||
sysfs_remove_group(&usbtouch->interface->dev.kobj, &mtouch_attr_group);
|
||||
kfree(priv);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1119,7 +1211,9 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
|
||||
.max_yc = 0x4000,
|
||||
.rept_size = 11,
|
||||
.read_data = mtouch_read_data,
|
||||
.alloc = mtouch_alloc,
|
||||
.init = mtouch_init,
|
||||
.exit = mtouch_exit,
|
||||
},
|
||||
#endif
|
||||
|
||||
|
Reference in New Issue
Block a user