Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (40 commits) Input: ADP5589 - new driver for I2C Keypad Decoder and I/O Expander Input: tsc2007 - add X, Y and Z fuzz factors to platform data Input: tsc2007 - add poll_period parameter to platform data Input: tsc2007 - add poll_delay parameter to platform data Input: tsc2007 - add max_rt parameter to platform data Input: tsc2007 - debounce pressure measurement Input: ad714x - fix captouch wheel option algorithm Input: ad714x - allow platform code to specify irqflags Input: ad714x - fix threshold and completion interrupt masks Input: ad714x - fix up input configuration Input: elantech - remove support for proprietary X driver Input: elantech - report multitouch with proper ABS_MT messages Input: elantech - export pressure and width when supported Input: elantech - describe further the protocol Input: atmel_tsadcc - correct call to input_free_device Input: add driver FSL MPR121 capacitive touch sensor Input: remove useless synchronize_rcu() calls Input: ads7846 - fix gpio_pendown configuration Input: ads7846 - add possibility to use external vref on ads7846 Input: rotary-encoder - add support for half-period encoders ...
This commit is contained in:
commit
4637f40f20
@ -34,7 +34,8 @@ Contents
|
||||
Currently the Linux Elantech touchpad driver is aware of two different
|
||||
hardware versions unimaginatively called version 1 and version 2. Version 1
|
||||
is found in "older" laptops and uses 4 bytes per packet. Version 2 seems to
|
||||
be introduced with the EeePC and uses 6 bytes per packet.
|
||||
be introduced with the EeePC and uses 6 bytes per packet, and provides
|
||||
additional features such as position of two fingers, and width of the touch.
|
||||
|
||||
The driver tries to support both hardware versions and should be compatible
|
||||
with the Xorg Synaptics touchpad driver and its graphical configuration
|
||||
@ -94,18 +95,44 @@ Currently the Linux Elantech touchpad driver provides two extra knobs under
|
||||
can check these bits and reject any packet that appears corrupted. Using
|
||||
this knob you can bypass that check.
|
||||
|
||||
It is not known yet whether hardware version 2 provides the same parity
|
||||
bits. Hence checking is disabled by default. Currently even turning it on
|
||||
will do nothing.
|
||||
|
||||
Hardware version 2 does not provide the same parity bits. Only some basic
|
||||
data consistency checking can be done. For now checking is disabled by
|
||||
default. Currently even turning it on will do nothing.
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
3. Differentiating hardware versions
|
||||
=================================
|
||||
|
||||
3. Hardware version 1
|
||||
To detect the hardware version, read the version number as param[0].param[1].param[2]
|
||||
|
||||
4 bytes version: (after the arrow is the name given in the Dell-provided driver)
|
||||
02.00.22 => EF013
|
||||
02.06.00 => EF019
|
||||
In the wild, there appear to be more versions, such as 00.01.64, 01.00.21,
|
||||
02.00.00, 02.00.04, 02.00.06.
|
||||
|
||||
6 bytes:
|
||||
02.00.30 => EF113
|
||||
02.08.00 => EF023
|
||||
02.08.XX => EF123
|
||||
02.0B.00 => EF215
|
||||
04.01.XX => Scroll_EF051
|
||||
04.02.XX => EF051
|
||||
In the wild, there appear to be more versions, such as 04.03.01, 04.04.11. There
|
||||
appears to be almost no difference, except for EF113, which does not report
|
||||
pressure/width and has different data consistency checks.
|
||||
|
||||
Probably all the versions with param[0] <= 01 can be considered as
|
||||
4 bytes/firmware 1. The versions < 02.08.00, with the exception of 02.00.30, as
|
||||
4 bytes/firmware 2. Everything >= 02.08.00 can be considered as 6 bytes.
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
4. Hardware version 1
|
||||
==================
|
||||
|
||||
3.1 Registers
|
||||
4.1 Registers
|
||||
~~~~~~~~~
|
||||
|
||||
By echoing a hexadecimal value to a register it contents can be altered.
|
||||
@ -168,7 +195,7 @@ For example:
|
||||
smart edge activation area width?
|
||||
|
||||
|
||||
3.2 Native relative mode 4 byte packet format
|
||||
4.2 Native relative mode 4 byte packet format
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
byte 0:
|
||||
@ -226,9 +253,13 @@ byte 3:
|
||||
positive = down
|
||||
|
||||
|
||||
3.3 Native absolute mode 4 byte packet format
|
||||
4.3 Native absolute mode 4 byte packet format
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
EF013 and EF019 have a special behaviour (due to a bug in the firmware?), and
|
||||
when 1 finger is touching, the first 2 position reports must be discarded.
|
||||
This counting is reset whenever a different number of fingers is reported.
|
||||
|
||||
byte 0:
|
||||
firmware version 1.x:
|
||||
|
||||
@ -279,11 +310,11 @@ byte 3:
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
4. Hardware version 2
|
||||
5. Hardware version 2
|
||||
==================
|
||||
|
||||
|
||||
4.1 Registers
|
||||
5.1 Registers
|
||||
~~~~~~~~~
|
||||
|
||||
By echoing a hexadecimal value to a register it contents can be altered.
|
||||
@ -316,16 +347,41 @@ For example:
|
||||
0x7f = never i.e. tap again to release)
|
||||
|
||||
|
||||
4.2 Native absolute mode 6 byte packet format
|
||||
5.2 Native absolute mode 6 byte packet format
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
5.2.1 Parity checking and packet re-synchronization
|
||||
There is no parity checking, however some consistency checks can be performed.
|
||||
|
||||
4.2.1 One finger touch
|
||||
For instance for EF113:
|
||||
SA1= packet[0];
|
||||
A1 = packet[1];
|
||||
B1 = packet[2];
|
||||
SB1= packet[3];
|
||||
C1 = packet[4];
|
||||
D1 = packet[5];
|
||||
if( (((SA1 & 0x3C) != 0x3C) && ((SA1 & 0xC0) != 0x80)) || // check Byte 1
|
||||
(((SA1 & 0x0C) != 0x0C) && ((SA1 & 0xC0) == 0x80)) || // check Byte 1 (one finger pressed)
|
||||
(((SA1 & 0xC0) != 0x80) && (( A1 & 0xF0) != 0x00)) || // check Byte 2
|
||||
(((SB1 & 0x3E) != 0x38) && ((SA1 & 0xC0) != 0x80)) || // check Byte 4
|
||||
(((SB1 & 0x0E) != 0x08) && ((SA1 & 0xC0) == 0x80)) || // check Byte 4 (one finger pressed)
|
||||
(((SA1 & 0xC0) != 0x80) && (( C1 & 0xF0) != 0x00)) ) // check Byte 5
|
||||
// error detected
|
||||
|
||||
For all the other ones, there are just a few constant bits:
|
||||
if( ((packet[0] & 0x0C) != 0x04) ||
|
||||
((packet[3] & 0x0f) != 0x02) )
|
||||
// error detected
|
||||
|
||||
|
||||
In case an error is detected, all the packets are shifted by one (and packet[0] is discarded).
|
||||
|
||||
5.2.1 One/Three finger touch
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
byte 0:
|
||||
|
||||
bit 7 6 5 4 3 2 1 0
|
||||
n1 n0 . . . . R L
|
||||
n1 n0 w3 w2 . . R L
|
||||
|
||||
L, R = 1 when Left, Right mouse button pressed
|
||||
n1..n0 = numbers of fingers on touchpad
|
||||
@ -333,24 +389,40 @@ byte 0:
|
||||
byte 1:
|
||||
|
||||
bit 7 6 5 4 3 2 1 0
|
||||
. . . . . x10 x9 x8
|
||||
p7 p6 p5 p4 . x10 x9 x8
|
||||
|
||||
byte 2:
|
||||
|
||||
bit 7 6 5 4 3 2 1 0
|
||||
x7 x6 x5 x4 x4 x2 x1 x0
|
||||
x7 x6 x5 x4 x3 x2 x1 x0
|
||||
|
||||
x10..x0 = absolute x value (horizontal)
|
||||
|
||||
byte 3:
|
||||
|
||||
bit 7 6 5 4 3 2 1 0
|
||||
. . . . . . . .
|
||||
n4 vf w1 w0 . . . b2
|
||||
|
||||
n4 = set if more than 3 fingers (only in 3 fingers mode)
|
||||
vf = a kind of flag ? (only on EF123, 0 when finger is over one
|
||||
of the buttons, 1 otherwise)
|
||||
w3..w0 = width of the finger touch (not EF113)
|
||||
b2 (on EF113 only, 0 otherwise), b2.R.L indicates one button pressed:
|
||||
0 = none
|
||||
1 = Left
|
||||
2 = Right
|
||||
3 = Middle (Left and Right)
|
||||
4 = Forward
|
||||
5 = Back
|
||||
6 = Another one
|
||||
7 = Another one
|
||||
|
||||
byte 4:
|
||||
|
||||
bit 7 6 5 4 3 2 1 0
|
||||
. . . . . . y9 y8
|
||||
p3 p1 p2 p0 . . y9 y8
|
||||
|
||||
p7..p0 = pressure (not EF113)
|
||||
|
||||
byte 5:
|
||||
|
||||
@ -363,6 +435,11 @@ byte 5:
|
||||
4.2.2 Two finger touch
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Note that the two pairs of coordinates are not exactly the coordinates of the
|
||||
two fingers, but only the pair of the lower-left and upper-right coordinates.
|
||||
So the actual fingers might be situated on the other diagonal of the square
|
||||
defined by these two points.
|
||||
|
||||
byte 0:
|
||||
|
||||
bit 7 6 5 4 3 2 1 0
|
||||
@ -376,14 +453,14 @@ byte 1:
|
||||
bit 7 6 5 4 3 2 1 0
|
||||
ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0
|
||||
|
||||
ax8..ax0 = first finger absolute x value
|
||||
ax8..ax0 = lower-left finger absolute x value
|
||||
|
||||
byte 2:
|
||||
|
||||
bit 7 6 5 4 3 2 1 0
|
||||
ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0
|
||||
|
||||
ay8..ay0 = first finger absolute y value
|
||||
ay8..ay0 = lower-left finger absolute y value
|
||||
|
||||
byte 3:
|
||||
|
||||
@ -395,11 +472,11 @@ byte 4:
|
||||
bit 7 6 5 4 3 2 1 0
|
||||
bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0
|
||||
|
||||
bx8..bx0 = second finger absolute x value
|
||||
bx8..bx0 = upper-right finger absolute x value
|
||||
|
||||
byte 5:
|
||||
|
||||
bit 7 6 5 4 3 2 1 0
|
||||
by7 by8 by5 by4 by3 by2 by1 by0
|
||||
|
||||
by8..by0 = second finger absolute y value
|
||||
by8..by0 = upper-right finger absolute y value
|
||||
|
@ -9,6 +9,9 @@ peripherals with two wires. The outputs are phase-shifted by 90 degrees
|
||||
and by triggering on falling and rising edges, the turn direction can
|
||||
be determined.
|
||||
|
||||
Some encoders have both outputs low in stable states, whereas others also have
|
||||
a stable state with both outputs high (half-period mode).
|
||||
|
||||
The phase diagram of these two outputs look like this:
|
||||
|
||||
_____ _____ _____
|
||||
@ -26,6 +29,8 @@ The phase diagram of these two outputs look like this:
|
||||
|<-------->|
|
||||
one step
|
||||
|
||||
|<-->|
|
||||
one step (half-period mode)
|
||||
|
||||
For more information, please see
|
||||
http://en.wikipedia.org/wiki/Rotary_encoder
|
||||
@ -34,6 +39,13 @@ For more information, please see
|
||||
1. Events / state machine
|
||||
-------------------------
|
||||
|
||||
In half-period mode, state a) and c) above are used to determine the
|
||||
rotational direction based on the last stable state. Events are reported in
|
||||
states b) and d) given that the new stable state is different from the last
|
||||
(i.e. the rotation was not reversed half-way).
|
||||
|
||||
Otherwise, the following apply:
|
||||
|
||||
a) Rising edge on channel A, channel B in low state
|
||||
This state is used to recognize a clockwise turn
|
||||
|
||||
@ -96,6 +108,7 @@ static struct rotary_encoder_platform_data my_rotary_encoder_info = {
|
||||
.gpio_b = GPIO_ROTARY_B,
|
||||
.inverted_a = 0,
|
||||
.inverted_b = 0,
|
||||
.half_period = false,
|
||||
};
|
||||
|
||||
static struct platform_device rotary_encoder_device = {
|
||||
|
@ -50,13 +50,11 @@ struct tegra_kbc_platform_data {
|
||||
unsigned int debounce_cnt;
|
||||
unsigned int repeat_cnt;
|
||||
|
||||
unsigned int wake_cnt; /* 0:wake on any key >1:wake on wake_cfg */
|
||||
const struct tegra_kbc_wake_key *wake_cfg;
|
||||
|
||||
struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO];
|
||||
const struct matrix_keymap_data *keymap_data;
|
||||
|
||||
bool wakeup;
|
||||
bool use_fn_map;
|
||||
bool use_ghost_filter;
|
||||
};
|
||||
#endif
|
||||
|
@ -41,6 +41,7 @@ struct evdev {
|
||||
struct evdev_client {
|
||||
unsigned int head;
|
||||
unsigned int tail;
|
||||
unsigned int packet_head; /* [future] position of the first element of next packet */
|
||||
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
|
||||
struct fasync_struct *fasync;
|
||||
struct evdev *evdev;
|
||||
@ -72,12 +73,16 @@ static void evdev_pass_event(struct evdev_client *client,
|
||||
client->buffer[client->tail].type = EV_SYN;
|
||||
client->buffer[client->tail].code = SYN_DROPPED;
|
||||
client->buffer[client->tail].value = 0;
|
||||
|
||||
client->packet_head = client->tail;
|
||||
}
|
||||
|
||||
if (event->type == EV_SYN && event->code == SYN_REPORT) {
|
||||
client->packet_head = client->head;
|
||||
kill_fasync(&client->fasync, SIGIO, POLL_IN);
|
||||
}
|
||||
|
||||
spin_unlock(&client->buffer_lock);
|
||||
|
||||
if (event->type == EV_SYN)
|
||||
kill_fasync(&client->fasync, SIGIO, POLL_IN);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -159,7 +164,6 @@ static int evdev_grab(struct evdev *evdev, struct evdev_client *client)
|
||||
return error;
|
||||
|
||||
rcu_assign_pointer(evdev->grab, client);
|
||||
synchronize_rcu();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -182,7 +186,6 @@ static void evdev_attach_client(struct evdev *evdev,
|
||||
spin_lock(&evdev->client_lock);
|
||||
list_add_tail_rcu(&client->node, &evdev->client_list);
|
||||
spin_unlock(&evdev->client_lock);
|
||||
synchronize_rcu();
|
||||
}
|
||||
|
||||
static void evdev_detach_client(struct evdev *evdev,
|
||||
@ -387,12 +390,12 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
|
||||
if (count < input_event_size())
|
||||
return -EINVAL;
|
||||
|
||||
if (client->head == client->tail && evdev->exist &&
|
||||
if (client->packet_head == client->tail && evdev->exist &&
|
||||
(file->f_flags & O_NONBLOCK))
|
||||
return -EAGAIN;
|
||||
|
||||
retval = wait_event_interruptible(evdev->wait,
|
||||
client->head != client->tail || !evdev->exist);
|
||||
client->packet_head != client->tail || !evdev->exist);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
@ -421,7 +424,7 @@ static unsigned int evdev_poll(struct file *file, poll_table *wait)
|
||||
poll_wait(file, &evdev->wait, wait);
|
||||
|
||||
mask = evdev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR;
|
||||
if (client->head != client->tail)
|
||||
if (client->packet_head != client->tail)
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
|
||||
return mask;
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/input-polldev.h>
|
||||
|
||||
MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
|
||||
@ -20,44 +21,6 @@ MODULE_DESCRIPTION("Generic implementation of a polled input device");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_VERSION("0.1");
|
||||
|
||||
static DEFINE_MUTEX(polldev_mutex);
|
||||
static int polldev_users;
|
||||
static struct workqueue_struct *polldev_wq;
|
||||
|
||||
static int input_polldev_start_workqueue(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = mutex_lock_interruptible(&polldev_mutex);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (!polldev_users) {
|
||||
polldev_wq = create_singlethread_workqueue("ipolldevd");
|
||||
if (!polldev_wq) {
|
||||
pr_err("failed to create ipolldevd workqueue\n");
|
||||
retval = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
polldev_users++;
|
||||
|
||||
out:
|
||||
mutex_unlock(&polldev_mutex);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void input_polldev_stop_workqueue(void)
|
||||
{
|
||||
mutex_lock(&polldev_mutex);
|
||||
|
||||
if (!--polldev_users)
|
||||
destroy_workqueue(polldev_wq);
|
||||
|
||||
mutex_unlock(&polldev_mutex);
|
||||
}
|
||||
|
||||
static void input_polldev_queue_work(struct input_polled_dev *dev)
|
||||
{
|
||||
unsigned long delay;
|
||||
@ -66,7 +29,7 @@ static void input_polldev_queue_work(struct input_polled_dev *dev)
|
||||
if (delay >= HZ)
|
||||
delay = round_jiffies_relative(delay);
|
||||
|
||||
queue_delayed_work(polldev_wq, &dev->work, delay);
|
||||
queue_delayed_work(system_freezable_wq, &dev->work, delay);
|
||||
}
|
||||
|
||||
static void input_polled_device_work(struct work_struct *work)
|
||||
@ -81,18 +44,13 @@ static void input_polled_device_work(struct work_struct *work)
|
||||
static int input_open_polled_device(struct input_dev *input)
|
||||
{
|
||||
struct input_polled_dev *dev = input_get_drvdata(input);
|
||||
int error;
|
||||
|
||||
error = input_polldev_start_workqueue();
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (dev->open)
|
||||
dev->open(dev);
|
||||
|
||||
/* Only start polling if polling is enabled */
|
||||
if (dev->poll_interval > 0)
|
||||
queue_delayed_work(polldev_wq, &dev->work, 0);
|
||||
queue_delayed_work(system_freezable_wq, &dev->work, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -102,13 +60,6 @@ static void input_close_polled_device(struct input_dev *input)
|
||||
struct input_polled_dev *dev = input_get_drvdata(input);
|
||||
|
||||
cancel_delayed_work_sync(&dev->work);
|
||||
/*
|
||||
* Clean up work struct to remove references to the workqueue.
|
||||
* It may be destroyed by the next call. This causes problems
|
||||
* at next device open-close in case of poll_interval == 0.
|
||||
*/
|
||||
INIT_DELAYED_WORK(&dev->work, dev->work.work.func);
|
||||
input_polldev_stop_workqueue();
|
||||
|
||||
if (dev->close)
|
||||
dev->close(dev);
|
||||
@ -295,4 +246,3 @@ void input_unregister_polled_device(struct input_polled_dev *dev)
|
||||
input_unregister_device(dev->input);
|
||||
}
|
||||
EXPORT_SYMBOL(input_unregister_polled_device);
|
||||
|
||||
|
@ -451,7 +451,6 @@ int input_grab_device(struct input_handle *handle)
|
||||
}
|
||||
|
||||
rcu_assign_pointer(dev->grab, handle);
|
||||
synchronize_rcu();
|
||||
|
||||
out:
|
||||
mutex_unlock(&dev->mutex);
|
||||
|
@ -180,7 +180,6 @@ static void joydev_attach_client(struct joydev *joydev,
|
||||
spin_lock(&joydev->client_lock);
|
||||
list_add_tail_rcu(&client->node, &joydev->client_list);
|
||||
spin_unlock(&joydev->client_lock);
|
||||
synchronize_rcu();
|
||||
}
|
||||
|
||||
static void joydev_detach_client(struct joydev *joydev,
|
||||
|
@ -32,6 +32,16 @@ config KEYBOARD_ADP5588
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called adp5588-keys.
|
||||
|
||||
config KEYBOARD_ADP5589
|
||||
tristate "ADP5589 I2C QWERTY Keypad and IO Expander"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you want to use a ADP5589 attached to your
|
||||
system I2C bus.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called adp5589-keys.
|
||||
|
||||
config KEYBOARD_AMIGA
|
||||
tristate "Amiga keyboard"
|
||||
depends on AMIGA
|
||||
@ -325,6 +335,18 @@ config KEYBOARD_MCS
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called mcs_touchkey.
|
||||
|
||||
config KEYBOARD_MPR121
|
||||
tristate "Freescale MPR121 Touchkey"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you have Freescale MPR121 touchkey controller
|
||||
chip in your system.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called mpr121_touchkey.
|
||||
|
||||
config KEYBOARD_IMX
|
||||
tristate "IMX keypad support"
|
||||
depends on ARCH_MXC
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
|
||||
obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
|
||||
obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
|
||||
@ -27,6 +28,7 @@ obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
|
||||
obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o
|
||||
obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o
|
||||
obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
|
||||
obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
|
||||
|
771
drivers/input/keyboard/adp5589-keys.c
Normal file
771
drivers/input/keyboard/adp5589-keys.c
Normal file
@ -0,0 +1,771 @@
|
||||
/*
|
||||
* Description: keypad driver for ADP5589
|
||||
* I2C QWERTY Keypad and IO Expander
|
||||
* Bugs: Enter bugs at http://blackfin.uclinux.org/
|
||||
*
|
||||
* Copyright (C) 2010-2011 Analog Devices Inc.
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/input/adp5589.h>
|
||||
|
||||
/* GENERAL_CFG Register */
|
||||
#define OSC_EN (1 << 7)
|
||||
#define CORE_CLK(x) (((x) & 0x3) << 5)
|
||||
#define LCK_TRK_LOGIC (1 << 4)
|
||||
#define LCK_TRK_GPI (1 << 3)
|
||||
#define INT_CFG (1 << 1)
|
||||
#define RST_CFG (1 << 0)
|
||||
|
||||
/* INT_EN Register */
|
||||
#define LOGIC2_IEN (1 << 5)
|
||||
#define LOGIC1_IEN (1 << 4)
|
||||
#define LOCK_IEN (1 << 3)
|
||||
#define OVRFLOW_IEN (1 << 2)
|
||||
#define GPI_IEN (1 << 1)
|
||||
#define EVENT_IEN (1 << 0)
|
||||
|
||||
/* Interrupt Status Register */
|
||||
#define LOGIC2_INT (1 << 5)
|
||||
#define LOGIC1_INT (1 << 4)
|
||||
#define LOCK_INT (1 << 3)
|
||||
#define OVRFLOW_INT (1 << 2)
|
||||
#define GPI_INT (1 << 1)
|
||||
#define EVENT_INT (1 << 0)
|
||||
|
||||
/* STATUS Register */
|
||||
|
||||
#define LOGIC2_STAT (1 << 7)
|
||||
#define LOGIC1_STAT (1 << 6)
|
||||
#define LOCK_STAT (1 << 5)
|
||||
#define KEC 0xF
|
||||
|
||||
/* PIN_CONFIG_D Register */
|
||||
#define C4_EXTEND_CFG (1 << 6) /* RESET2 */
|
||||
#define R4_EXTEND_CFG (1 << 5) /* RESET1 */
|
||||
|
||||
/* LOCK_CFG */
|
||||
#define LOCK_EN (1 << 0)
|
||||
|
||||
#define PTIME_MASK 0x3
|
||||
#define LTIME_MASK 0x3
|
||||
|
||||
/* Key Event Register xy */
|
||||
#define KEY_EV_PRESSED (1 << 7)
|
||||
#define KEY_EV_MASK (0x7F)
|
||||
|
||||
#define KEYP_MAX_EVENT 16
|
||||
|
||||
#define MAXGPIO 19
|
||||
#define ADP_BANK(offs) ((offs) >> 3)
|
||||
#define ADP_BIT(offs) (1u << ((offs) & 0x7))
|
||||
|
||||
struct adp5589_kpad {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
unsigned short keycode[ADP5589_KEYMAPSIZE];
|
||||
const struct adp5589_gpi_map *gpimap;
|
||||
unsigned short gpimapsize;
|
||||
unsigned extend_cfg;
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
unsigned char gpiomap[MAXGPIO];
|
||||
bool export_gpio;
|
||||
struct gpio_chip gc;
|
||||
struct mutex gpio_lock; /* Protect cached dir, dat_out */
|
||||
u8 dat_out[3];
|
||||
u8 dir[3];
|
||||
#endif
|
||||
};
|
||||
|
||||
static int adp5589_read(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
int ret = i2c_smbus_read_byte_data(client, reg);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "Read Error\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adp5589_write(struct i2c_client *client, u8 reg, u8 val)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(client, reg, val);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
static int adp5589_gpio_get_value(struct gpio_chip *chip, unsigned off)
|
||||
{
|
||||
struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc);
|
||||
unsigned int bank = ADP_BANK(kpad->gpiomap[off]);
|
||||
unsigned int bit = ADP_BIT(kpad->gpiomap[off]);
|
||||
|
||||
return !!(adp5589_read(kpad->client, ADP5589_GPI_STATUS_A + bank) &
|
||||
bit);
|
||||
}
|
||||
|
||||
static void adp5589_gpio_set_value(struct gpio_chip *chip,
|
||||
unsigned off, int val)
|
||||
{
|
||||
struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc);
|
||||
unsigned int bank = ADP_BANK(kpad->gpiomap[off]);
|
||||
unsigned int bit = ADP_BIT(kpad->gpiomap[off]);
|
||||
|
||||
mutex_lock(&kpad->gpio_lock);
|
||||
|
||||
if (val)
|
||||
kpad->dat_out[bank] |= bit;
|
||||
else
|
||||
kpad->dat_out[bank] &= ~bit;
|
||||
|
||||
adp5589_write(kpad->client, ADP5589_GPO_DATA_OUT_A + bank,
|
||||
kpad->dat_out[bank]);
|
||||
|
||||
mutex_unlock(&kpad->gpio_lock);
|
||||
}
|
||||
|
||||
static int adp5589_gpio_direction_input(struct gpio_chip *chip, unsigned off)
|
||||
{
|
||||
struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc);
|
||||
unsigned int bank = ADP_BANK(kpad->gpiomap[off]);
|
||||
unsigned int bit = ADP_BIT(kpad->gpiomap[off]);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&kpad->gpio_lock);
|
||||
|
||||
kpad->dir[bank] &= ~bit;
|
||||
ret = adp5589_write(kpad->client, ADP5589_GPIO_DIRECTION_A + bank,
|
||||
kpad->dir[bank]);
|
||||
|
||||
mutex_unlock(&kpad->gpio_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adp5589_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned off, int val)
|
||||
{
|
||||
struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc);
|
||||
unsigned int bank = ADP_BANK(kpad->gpiomap[off]);
|
||||
unsigned int bit = ADP_BIT(kpad->gpiomap[off]);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&kpad->gpio_lock);
|
||||
|
||||
kpad->dir[bank] |= bit;
|
||||
|
||||
if (val)
|
||||
kpad->dat_out[bank] |= bit;
|
||||
else
|
||||
kpad->dat_out[bank] &= ~bit;
|
||||
|
||||
ret = adp5589_write(kpad->client, ADP5589_GPO_DATA_OUT_A + bank,
|
||||
kpad->dat_out[bank]);
|
||||
ret |= adp5589_write(kpad->client, ADP5589_GPIO_DIRECTION_A + bank,
|
||||
kpad->dir[bank]);
|
||||
|
||||
mutex_unlock(&kpad->gpio_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devinit adp5589_build_gpiomap(struct adp5589_kpad *kpad,
|
||||
const struct adp5589_kpad_platform_data *pdata)
|
||||
{
|
||||
bool pin_used[MAXGPIO];
|
||||
int n_unused = 0;
|
||||
int i;
|
||||
|
||||
memset(pin_used, false, sizeof(pin_used));
|
||||
|
||||
for (i = 0; i < MAXGPIO; i++)
|
||||
if (pdata->keypad_en_mask & (1 << i))
|
||||
pin_used[i] = true;
|
||||
|
||||
for (i = 0; i < kpad->gpimapsize; i++)
|
||||
pin_used[kpad->gpimap[i].pin - ADP5589_GPI_PIN_BASE] = true;
|
||||
|
||||
if (kpad->extend_cfg & R4_EXTEND_CFG)
|
||||
pin_used[4] = true;
|
||||
|
||||
if (kpad->extend_cfg & C4_EXTEND_CFG)
|
||||
pin_used[12] = true;
|
||||
|
||||
for (i = 0; i < MAXGPIO; i++)
|
||||
if (!pin_used[i])
|
||||
kpad->gpiomap[n_unused++] = i;
|
||||
|
||||
return n_unused;
|
||||
}
|
||||
|
||||
static int __devinit adp5589_gpio_add(struct adp5589_kpad *kpad)
|
||||
{
|
||||
struct device *dev = &kpad->client->dev;
|
||||
const struct adp5589_kpad_platform_data *pdata = dev->platform_data;
|
||||
const struct adp5589_gpio_platform_data *gpio_data = pdata->gpio_data;
|
||||
int i, error;
|
||||
|
||||
if (!gpio_data)
|
||||
return 0;
|
||||
|
||||
kpad->gc.ngpio = adp5589_build_gpiomap(kpad, pdata);
|
||||
if (kpad->gc.ngpio == 0) {
|
||||
dev_info(dev, "No unused gpios left to export\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
kpad->export_gpio = true;
|
||||
|
||||
kpad->gc.direction_input = adp5589_gpio_direction_input;
|
||||
kpad->gc.direction_output = adp5589_gpio_direction_output;
|
||||
kpad->gc.get = adp5589_gpio_get_value;
|
||||
kpad->gc.set = adp5589_gpio_set_value;
|
||||
kpad->gc.can_sleep = 1;
|
||||
|
||||
kpad->gc.base = gpio_data->gpio_start;
|
||||
kpad->gc.label = kpad->client->name;
|
||||
kpad->gc.owner = THIS_MODULE;
|
||||
|
||||
mutex_init(&kpad->gpio_lock);
|
||||
|
||||
error = gpiochip_add(&kpad->gc);
|
||||
if (error) {
|
||||
dev_err(dev, "gpiochip_add failed, err: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
for (i = 0; i <= ADP_BANK(MAXGPIO); i++) {
|
||||
kpad->dat_out[i] = adp5589_read(kpad->client,
|
||||
ADP5589_GPO_DATA_OUT_A + i);
|
||||
kpad->dir[i] = adp5589_read(kpad->client,
|
||||
ADP5589_GPIO_DIRECTION_A + i);
|
||||
}
|
||||
|
||||
if (gpio_data->setup) {
|
||||
error = gpio_data->setup(kpad->client,
|
||||
kpad->gc.base, kpad->gc.ngpio,
|
||||
gpio_data->context);
|
||||
if (error)
|
||||
dev_warn(dev, "setup failed, %d\n", error);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __devexit adp5589_gpio_remove(struct adp5589_kpad *kpad)
|
||||
{
|
||||
struct device *dev = &kpad->client->dev;
|
||||
const struct adp5589_kpad_platform_data *pdata = dev->platform_data;
|
||||
const struct adp5589_gpio_platform_data *gpio_data = pdata->gpio_data;
|
||||
int error;
|
||||
|
||||
if (!kpad->export_gpio)
|
||||
return;
|
||||
|
||||
if (gpio_data->teardown) {
|
||||
error = gpio_data->teardown(kpad->client,
|
||||
kpad->gc.base, kpad->gc.ngpio,
|
||||
gpio_data->context);
|
||||
if (error)
|
||||
dev_warn(dev, "teardown failed %d\n", error);
|
||||
}
|
||||
|
||||
error = gpiochip_remove(&kpad->gc);
|
||||
if (error)
|
||||
dev_warn(dev, "gpiochip_remove failed %d\n", error);
|
||||
}
|
||||
#else
|
||||
static inline int adp5589_gpio_add(struct adp5589_kpad *kpad)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void adp5589_gpio_remove(struct adp5589_kpad *kpad)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static void adp5589_report_switches(struct adp5589_kpad *kpad,
|
||||
int key, int key_val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < kpad->gpimapsize; i++) {
|
||||
if (key_val == kpad->gpimap[i].pin) {
|
||||
input_report_switch(kpad->input,
|
||||
kpad->gpimap[i].sw_evt,
|
||||
key & KEY_EV_PRESSED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void adp5589_report_events(struct adp5589_kpad *kpad, int ev_cnt)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ev_cnt; i++) {
|
||||
int key = adp5589_read(kpad->client, ADP5589_FIFO_1 + i);
|
||||
int key_val = key & KEY_EV_MASK;
|
||||
|
||||
if (key_val >= ADP5589_GPI_PIN_BASE &&
|
||||
key_val <= ADP5589_GPI_PIN_END) {
|
||||
adp5589_report_switches(kpad, key, key_val);
|
||||
} else {
|
||||
input_report_key(kpad->input,
|
||||
kpad->keycode[key_val - 1],
|
||||
key & KEY_EV_PRESSED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t adp5589_irq(int irq, void *handle)
|
||||
{
|
||||
struct adp5589_kpad *kpad = handle;
|
||||
struct i2c_client *client = kpad->client;
|
||||
int status, ev_cnt;
|
||||
|
||||
status = adp5589_read(client, ADP5589_INT_STATUS);
|
||||
|
||||
if (status & OVRFLOW_INT) /* Unlikely and should never happen */
|
||||
dev_err(&client->dev, "Event Overflow Error\n");
|
||||
|
||||
if (status & EVENT_INT) {
|
||||
ev_cnt = adp5589_read(client, ADP5589_STATUS) & KEC;
|
||||
if (ev_cnt) {
|
||||
adp5589_report_events(kpad, ev_cnt);
|
||||
input_sync(kpad->input);
|
||||
}
|
||||
}
|
||||
|
||||
adp5589_write(client, ADP5589_INT_STATUS, status); /* Status is W1C */
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit adp5589_get_evcode(struct adp5589_kpad *kpad, unsigned short key)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ADP5589_KEYMAPSIZE; i++)
|
||||
if (key == kpad->keycode[i])
|
||||
return (i + 1) | KEY_EV_PRESSED;
|
||||
|
||||
dev_err(&kpad->client->dev, "RESET/UNLOCK key not in keycode map\n");
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int __devinit adp5589_setup(struct adp5589_kpad *kpad)
|
||||
{
|
||||
struct i2c_client *client = kpad->client;
|
||||
const struct adp5589_kpad_platform_data *pdata =
|
||||
client->dev.platform_data;
|
||||
int i, ret;
|
||||
unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0;
|
||||
unsigned char pull_mask = 0;
|
||||
|
||||
ret = adp5589_write(client, ADP5589_PIN_CONFIG_A,
|
||||
pdata->keypad_en_mask & 0xFF);
|
||||
ret |= adp5589_write(client, ADP5589_PIN_CONFIG_B,
|
||||
(pdata->keypad_en_mask >> 8) & 0xFF);
|
||||
ret |= adp5589_write(client, ADP5589_PIN_CONFIG_C,
|
||||
(pdata->keypad_en_mask >> 16) & 0xFF);
|
||||
|
||||
if (pdata->en_keylock) {
|
||||
ret |= adp5589_write(client, ADP5589_UNLOCK1,
|
||||
pdata->unlock_key1);
|
||||
ret |= adp5589_write(client, ADP5589_UNLOCK2,
|
||||
pdata->unlock_key2);
|
||||
ret |= adp5589_write(client, ADP5589_UNLOCK_TIMERS,
|
||||
pdata->unlock_timer & LTIME_MASK);
|
||||
ret |= adp5589_write(client, ADP5589_LOCK_CFG, LOCK_EN);
|
||||
}
|
||||
|
||||
for (i = 0; i < KEYP_MAX_EVENT; i++)
|
||||
ret |= adp5589_read(client, ADP5589_FIFO_1 + i);
|
||||
|
||||
for (i = 0; i < pdata->gpimapsize; i++) {
|
||||
unsigned short pin = pdata->gpimap[i].pin;
|
||||
|
||||
if (pin <= ADP5589_GPI_PIN_ROW_END) {
|
||||
evt_mode1 |= (1 << (pin - ADP5589_GPI_PIN_ROW_BASE));
|
||||
} else {
|
||||
evt_mode2 |=
|
||||
((1 << (pin - ADP5589_GPI_PIN_COL_BASE)) & 0xFF);
|
||||
evt_mode3 |=
|
||||
((1 << (pin - ADP5589_GPI_PIN_COL_BASE)) >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
if (pdata->gpimapsize) {
|
||||
ret |= adp5589_write(client, ADP5589_GPI_EVENT_EN_A, evt_mode1);
|
||||
ret |= adp5589_write(client, ADP5589_GPI_EVENT_EN_B, evt_mode2);
|
||||
ret |= adp5589_write(client, ADP5589_GPI_EVENT_EN_C, evt_mode3);
|
||||
}
|
||||
|
||||
if (pdata->pull_dis_mask & pdata->pullup_en_100k &
|
||||
pdata->pullup_en_300k & pdata->pulldown_en_300k)
|
||||
dev_warn(&client->dev, "Conflicting pull resistor config\n");
|
||||
|
||||
for (i = 0; i < MAXGPIO; i++) {
|
||||
unsigned val = 0;
|
||||
|
||||
if (pdata->pullup_en_300k & (1 << i))
|
||||
val = 0;
|
||||
else if (pdata->pulldown_en_300k & (1 << i))
|
||||
val = 1;
|
||||
else if (pdata->pullup_en_100k & (1 << i))
|
||||
val = 2;
|
||||
else if (pdata->pull_dis_mask & (1 << i))
|
||||
val = 3;
|
||||
|
||||
pull_mask |= val << (2 * (i & 0x3));
|
||||
|
||||
if ((i & 0x3) == 0x3 || i == MAXGPIO - 1) {
|
||||
ret |= adp5589_write(client,
|
||||
ADP5589_RPULL_CONFIG_A + (i >> 2),
|
||||
pull_mask);
|
||||
pull_mask = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (pdata->reset1_key_1 && pdata->reset1_key_2 && pdata->reset1_key_3) {
|
||||
ret |= adp5589_write(client, ADP5589_RESET1_EVENT_A,
|
||||
adp5589_get_evcode(kpad,
|
||||
pdata->reset1_key_1));
|
||||
ret |= adp5589_write(client, ADP5589_RESET1_EVENT_B,
|
||||
adp5589_get_evcode(kpad,
|
||||
pdata->reset1_key_2));
|
||||
ret |= adp5589_write(client, ADP5589_RESET1_EVENT_C,
|
||||
adp5589_get_evcode(kpad,
|
||||
pdata->reset1_key_3));
|
||||
kpad->extend_cfg |= R4_EXTEND_CFG;
|
||||
}
|
||||
|
||||
if (pdata->reset2_key_1 && pdata->reset2_key_2) {
|
||||
ret |= adp5589_write(client, ADP5589_RESET2_EVENT_A,
|
||||
adp5589_get_evcode(kpad,
|
||||
pdata->reset2_key_1));
|
||||
ret |= adp5589_write(client, ADP5589_RESET2_EVENT_B,
|
||||
adp5589_get_evcode(kpad,
|
||||
pdata->reset2_key_2));
|
||||
kpad->extend_cfg |= C4_EXTEND_CFG;
|
||||
}
|
||||
|
||||
if (kpad->extend_cfg) {
|
||||
ret |= adp5589_write(client, ADP5589_RESET_CFG,
|
||||
pdata->reset_cfg);
|
||||
ret |= adp5589_write(client, ADP5589_PIN_CONFIG_D,
|
||||
kpad->extend_cfg);
|
||||
}
|
||||
|
||||
for (i = 0; i <= ADP_BANK(MAXGPIO); i++)
|
||||
ret |= adp5589_write(client, ADP5589_DEBOUNCE_DIS_A + i,
|
||||
pdata->debounce_dis_mask >> (i * 8));
|
||||
|
||||
ret |= adp5589_write(client, ADP5589_POLL_PTIME_CFG,
|
||||
pdata->scan_cycle_time & PTIME_MASK);
|
||||
ret |= adp5589_write(client, ADP5589_INT_STATUS, LOGIC2_INT |
|
||||
LOGIC1_INT | OVRFLOW_INT | LOCK_INT |
|
||||
GPI_INT | EVENT_INT); /* Status is W1C */
|
||||
|
||||
ret |= adp5589_write(client, ADP5589_GENERAL_CFG,
|
||||
INT_CFG | OSC_EN | CORE_CLK(3));
|
||||
ret |= adp5589_write(client, ADP5589_INT_EN,
|
||||
OVRFLOW_IEN | GPI_IEN | EVENT_IEN);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Write Error\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __devinit adp5589_report_switch_state(struct adp5589_kpad *kpad)
|
||||
{
|
||||
int gpi_stat1 = adp5589_read(kpad->client, ADP5589_GPI_STATUS_A);
|
||||
int gpi_stat2 = adp5589_read(kpad->client, ADP5589_GPI_STATUS_B);
|
||||
int gpi_stat3 = adp5589_read(kpad->client, ADP5589_GPI_STATUS_C);
|
||||
int gpi_stat_tmp, pin_loc;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < kpad->gpimapsize; i++) {
|
||||
unsigned short pin = kpad->gpimap[i].pin;
|
||||
|
||||
if (pin <= ADP5589_GPI_PIN_ROW_END) {
|
||||
gpi_stat_tmp = gpi_stat1;
|
||||
pin_loc = pin - ADP5589_GPI_PIN_ROW_BASE;
|
||||
} else if ((pin - ADP5589_GPI_PIN_COL_BASE) < 8) {
|
||||
gpi_stat_tmp = gpi_stat2;
|
||||
pin_loc = pin - ADP5589_GPI_PIN_COL_BASE;
|
||||
} else {
|
||||
gpi_stat_tmp = gpi_stat3;
|
||||
pin_loc = pin - ADP5589_GPI_PIN_COL_BASE - 8;
|
||||
}
|
||||
|
||||
if (gpi_stat_tmp < 0) {
|
||||
dev_err(&kpad->client->dev,
|
||||
"Can't read GPIO_DAT_STAT switch"
|
||||
" %d default to OFF\n", pin);
|
||||
gpi_stat_tmp = 0;
|
||||
}
|
||||
|
||||
input_report_switch(kpad->input,
|
||||
kpad->gpimap[i].sw_evt,
|
||||
!(gpi_stat_tmp & (1 << pin_loc)));
|
||||
}
|
||||
|
||||
input_sync(kpad->input);
|
||||
}
|
||||
|
||||
static int __devinit adp5589_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct adp5589_kpad *kpad;
|
||||
const struct adp5589_kpad_platform_data *pdata;
|
||||
struct input_dev *input;
|
||||
unsigned int revid;
|
||||
int ret, i;
|
||||
int error;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||
dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
pdata = client->dev.platform_data;
|
||||
if (!pdata) {
|
||||
dev_err(&client->dev, "no platform data?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!((pdata->keypad_en_mask & 0xFF) &&
|
||||
(pdata->keypad_en_mask >> 8)) || !pdata->keymap) {
|
||||
dev_err(&client->dev, "no rows, cols or keymap from pdata\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pdata->keymapsize != ADP5589_KEYMAPSIZE) {
|
||||
dev_err(&client->dev, "invalid keymapsize\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!pdata->gpimap && pdata->gpimapsize) {
|
||||
dev_err(&client->dev, "invalid gpimap from pdata\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pdata->gpimapsize > ADP5589_GPIMAPSIZE_MAX) {
|
||||
dev_err(&client->dev, "invalid gpimapsize\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < pdata->gpimapsize; i++) {
|
||||
unsigned short pin = pdata->gpimap[i].pin;
|
||||
|
||||
if (pin < ADP5589_GPI_PIN_BASE || pin > ADP5589_GPI_PIN_END) {
|
||||
dev_err(&client->dev, "invalid gpi pin data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((1 << (pin - ADP5589_GPI_PIN_ROW_BASE)) &
|
||||
pdata->keypad_en_mask) {
|
||||
dev_err(&client->dev, "invalid gpi row/col data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!client->irq) {
|
||||
dev_err(&client->dev, "no IRQ?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
kpad = kzalloc(sizeof(*kpad), GFP_KERNEL);
|
||||
input = input_allocate_device();
|
||||
if (!kpad || !input) {
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
kpad->client = client;
|
||||
kpad->input = input;
|
||||
|
||||
ret = adp5589_read(client, ADP5589_ID);
|
||||
if (ret < 0) {
|
||||
error = ret;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
revid = (u8) ret & ADP5589_DEVICE_ID_MASK;
|
||||
|
||||
input->name = client->name;
|
||||
input->phys = "adp5589-keys/input0";
|
||||
input->dev.parent = &client->dev;
|
||||
|
||||
input_set_drvdata(input, kpad);
|
||||
|
||||
input->id.bustype = BUS_I2C;
|
||||
input->id.vendor = 0x0001;
|
||||
input->id.product = 0x0001;
|
||||
input->id.version = revid;
|
||||
|
||||
input->keycodesize = sizeof(kpad->keycode[0]);
|
||||
input->keycodemax = pdata->keymapsize;
|
||||
input->keycode = kpad->keycode;
|
||||
|
||||
memcpy(kpad->keycode, pdata->keymap,
|
||||
pdata->keymapsize * input->keycodesize);
|
||||
|
||||
kpad->gpimap = pdata->gpimap;
|
||||
kpad->gpimapsize = pdata->gpimapsize;
|
||||
|
||||
/* 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++)
|
||||
__set_bit(kpad->keycode[i] & KEY_MAX, input->keybit);
|
||||
__clear_bit(KEY_RESERVED, input->keybit);
|
||||
|
||||
if (kpad->gpimapsize)
|
||||
__set_bit(EV_SW, input->evbit);
|
||||
for (i = 0; i < kpad->gpimapsize; i++)
|
||||
__set_bit(kpad->gpimap[i].sw_evt, input->swbit);
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "unable to register input device\n");
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
error = request_threaded_irq(client->irq, NULL, adp5589_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
client->dev.driver->name, kpad);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "irq %d busy?\n", client->irq);
|
||||
goto err_unreg_dev;
|
||||
}
|
||||
|
||||
error = adp5589_setup(kpad);
|
||||
if (error)
|
||||
goto err_free_irq;
|
||||
|
||||
if (kpad->gpimapsize)
|
||||
adp5589_report_switch_state(kpad);
|
||||
|
||||
error = adp5589_gpio_add(kpad);
|
||||
if (error)
|
||||
goto err_free_irq;
|
||||
|
||||
device_init_wakeup(&client->dev, 1);
|
||||
i2c_set_clientdata(client, kpad);
|
||||
|
||||
dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq);
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(client->irq, kpad);
|
||||
err_unreg_dev:
|
||||
input_unregister_device(input);
|
||||
input = NULL;
|
||||
err_free_mem:
|
||||
input_free_device(input);
|
||||
kfree(kpad);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit adp5589_remove(struct i2c_client *client)
|
||||
{
|
||||
struct adp5589_kpad *kpad = i2c_get_clientdata(client);
|
||||
|
||||
adp5589_write(client, ADP5589_GENERAL_CFG, 0);
|
||||
free_irq(client->irq, kpad);
|
||||
input_unregister_device(kpad->input);
|
||||
adp5589_gpio_remove(kpad);
|
||||
kfree(kpad);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int adp5589_suspend(struct device *dev)
|
||||
{
|
||||
struct adp5589_kpad *kpad = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = kpad->client;
|
||||
|
||||
disable_irq(client->irq);
|
||||
|
||||
if (device_may_wakeup(&client->dev))
|
||||
enable_irq_wake(client->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adp5589_resume(struct device *dev)
|
||||
{
|
||||
struct adp5589_kpad *kpad = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = kpad->client;
|
||||
|
||||
if (device_may_wakeup(&client->dev))
|
||||
disable_irq_wake(client->irq);
|
||||
|
||||
enable_irq(client->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(adp5589_dev_pm_ops, adp5589_suspend, adp5589_resume);
|
||||
|
||||
static const struct i2c_device_id adp5589_id[] = {
|
||||
{"adp5589-keys", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, adp5589_id);
|
||||
|
||||
static struct i2c_driver adp5589_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &adp5589_dev_pm_ops,
|
||||
},
|
||||
.probe = adp5589_probe,
|
||||
.remove = __devexit_p(adp5589_remove),
|
||||
.id_table = adp5589_id,
|
||||
};
|
||||
|
||||
static int __init adp5589_init(void)
|
||||
{
|
||||
return i2c_add_driver(&adp5589_driver);
|
||||
}
|
||||
module_init(adp5589_init);
|
||||
|
||||
static void __exit adp5589_exit(void)
|
||||
{
|
||||
i2c_del_driver(&adp5589_driver);
|
||||
}
|
||||
module_exit(adp5589_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("ADP5589 Keypad driver");
|
@ -324,7 +324,12 @@ static void gpio_keys_report_event(struct gpio_button_data *bdata)
|
||||
unsigned int type = button->type ?: EV_KEY;
|
||||
int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low;
|
||||
|
||||
input_event(input, type, button->code, !!state);
|
||||
if (type == EV_ABS) {
|
||||
if (state)
|
||||
input_event(input, type, button->code, button->value);
|
||||
} else {
|
||||
input_event(input, type, button->code, !!state);
|
||||
}
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
@ -363,7 +368,7 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
|
||||
struct gpio_button_data *bdata,
|
||||
struct gpio_keys_button *button)
|
||||
{
|
||||
char *desc = button->desc ? button->desc : "gpio_keys";
|
||||
const char *desc = button->desc ? button->desc : "gpio_keys";
|
||||
struct device *dev = &pdev->dev;
|
||||
unsigned long irqflags;
|
||||
int irq, error;
|
||||
@ -468,7 +473,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
input_set_drvdata(input, ddata);
|
||||
|
||||
input->name = pdev->name;
|
||||
input->name = pdata->name ? : pdev->name;
|
||||
input->phys = "gpio-keys/input0";
|
||||
input->dev.parent = &pdev->dev;
|
||||
input->open = gpio_keys_open;
|
||||
|
339
drivers/input/keyboard/mpr121_touchkey.c
Normal file
339
drivers/input/keyboard/mpr121_touchkey.c
Normal file
@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Touchkey driver for Freescale MPR121 Controllor
|
||||
*
|
||||
* Copyright (C) 2011 Freescale Semiconductor, Inc.
|
||||
* Author: Zhang Jiejing <jiejing.zhang@freescale.com>
|
||||
*
|
||||
* Based on mcs_touchkey.c
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c/mpr121_touchkey.h>
|
||||
|
||||
/* Register definitions */
|
||||
#define ELE_TOUCH_STATUS_0_ADDR 0x0
|
||||
#define ELE_TOUCH_STATUS_1_ADDR 0X1
|
||||
#define MHD_RISING_ADDR 0x2b
|
||||
#define NHD_RISING_ADDR 0x2c
|
||||
#define NCL_RISING_ADDR 0x2d
|
||||
#define FDL_RISING_ADDR 0x2e
|
||||
#define MHD_FALLING_ADDR 0x2f
|
||||
#define NHD_FALLING_ADDR 0x30
|
||||
#define NCL_FALLING_ADDR 0x31
|
||||
#define FDL_FALLING_ADDR 0x32
|
||||
#define ELE0_TOUCH_THRESHOLD_ADDR 0x41
|
||||
#define ELE0_RELEASE_THRESHOLD_ADDR 0x42
|
||||
#define AFE_CONF_ADDR 0x5c
|
||||
#define FILTER_CONF_ADDR 0x5d
|
||||
|
||||
/*
|
||||
* ELECTRODE_CONF_ADDR: This register configures the number of
|
||||
* enabled capacitance sensing inputs and its run/suspend mode.
|
||||
*/
|
||||
#define ELECTRODE_CONF_ADDR 0x5e
|
||||
#define AUTO_CONFIG_CTRL_ADDR 0x7b
|
||||
#define AUTO_CONFIG_USL_ADDR 0x7d
|
||||
#define AUTO_CONFIG_LSL_ADDR 0x7e
|
||||
#define AUTO_CONFIG_TL_ADDR 0x7f
|
||||
|
||||
/* Threshold of touch/release trigger */
|
||||
#define TOUCH_THRESHOLD 0x0f
|
||||
#define RELEASE_THRESHOLD 0x0a
|
||||
/* Masks for touch and release triggers */
|
||||
#define TOUCH_STATUS_MASK 0xfff
|
||||
/* MPR121 has 12 keys */
|
||||
#define MPR121_MAX_KEY_COUNT 12
|
||||
|
||||
struct mpr121_touchkey {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input_dev;
|
||||
unsigned int key_val;
|
||||
unsigned int statusbits;
|
||||
unsigned int keycount;
|
||||
u16 keycodes[MPR121_MAX_KEY_COUNT];
|
||||
};
|
||||
|
||||
struct mpr121_init_register {
|
||||
int addr;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
static const struct mpr121_init_register init_reg_table[] __devinitconst = {
|
||||
{ MHD_RISING_ADDR, 0x1 },
|
||||
{ NHD_RISING_ADDR, 0x1 },
|
||||
{ MHD_FALLING_ADDR, 0x1 },
|
||||
{ NHD_FALLING_ADDR, 0x1 },
|
||||
{ NCL_FALLING_ADDR, 0xff },
|
||||
{ FDL_FALLING_ADDR, 0x02 },
|
||||
{ FILTER_CONF_ADDR, 0x04 },
|
||||
{ AFE_CONF_ADDR, 0x0b },
|
||||
{ AUTO_CONFIG_CTRL_ADDR, 0x0b },
|
||||
};
|
||||
|
||||
static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct mpr121_touchkey *mpr121 = dev_id;
|
||||
struct i2c_client *client = mpr121->client;
|
||||
struct input_dev *input = mpr121->input_dev;
|
||||
unsigned int key_num, key_val, pressed;
|
||||
int reg;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_1_ADDR);
|
||||
if (reg < 0) {
|
||||
dev_err(&client->dev, "i2c read error [%d]\n", reg);
|
||||
goto out;
|
||||
}
|
||||
|
||||
reg <<= 8;
|
||||
reg |= i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_0_ADDR);
|
||||
if (reg < 0) {
|
||||
dev_err(&client->dev, "i2c read error [%d]\n", reg);
|
||||
goto out;
|
||||
}
|
||||
|
||||
reg &= TOUCH_STATUS_MASK;
|
||||
/* use old press bit to figure out which bit changed */
|
||||
key_num = ffs(reg ^ mpr121->statusbits) - 1;
|
||||
pressed = reg & (1 << key_num);
|
||||
mpr121->statusbits = reg;
|
||||
|
||||
key_val = mpr121->keycodes[key_num];
|
||||
|
||||
input_event(input, EV_MSC, MSC_SCAN, key_num);
|
||||
input_report_key(input, key_val, pressed);
|
||||
input_sync(input);
|
||||
|
||||
dev_dbg(&client->dev, "key %d %d %s\n", key_num, key_val,
|
||||
pressed ? "pressed" : "released");
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit mpr121_phys_init(const struct mpr121_platform_data *pdata,
|
||||
struct mpr121_touchkey *mpr121,
|
||||
struct i2c_client *client)
|
||||
{
|
||||
const struct mpr121_init_register *reg;
|
||||
unsigned char usl, lsl, tl;
|
||||
int i, t, vdd, ret;
|
||||
|
||||
/* Set up touch/release threshold for ele0-ele11 */
|
||||
for (i = 0; i <= MPR121_MAX_KEY_COUNT; i++) {
|
||||
t = ELE0_TOUCH_THRESHOLD_ADDR + (i * 2);
|
||||
ret = i2c_smbus_write_byte_data(client, t, TOUCH_THRESHOLD);
|
||||
if (ret < 0)
|
||||
goto err_i2c_write;
|
||||
ret = i2c_smbus_write_byte_data(client, t + 1,
|
||||
RELEASE_THRESHOLD);
|
||||
if (ret < 0)
|
||||
goto err_i2c_write;
|
||||
}
|
||||
|
||||
/* Set up init register */
|
||||
for (i = 0; i < ARRAY_SIZE(init_reg_table); i++) {
|
||||
reg = &init_reg_table[i];
|
||||
ret = i2c_smbus_write_byte_data(client, reg->addr, reg->val);
|
||||
if (ret < 0)
|
||||
goto err_i2c_write;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Capacitance on sensing input varies and needs to be compensated.
|
||||
* The internal MPR121-auto-configuration can do this if it's
|
||||
* registers are set properly (based on pdata->vdd_uv).
|
||||
*/
|
||||
vdd = pdata->vdd_uv / 1000;
|
||||
usl = ((vdd - 700) * 256) / vdd;
|
||||
lsl = (usl * 65) / 100;
|
||||
tl = (usl * 90) / 100;
|
||||
ret = i2c_smbus_write_byte_data(client, AUTO_CONFIG_USL_ADDR, usl);
|
||||
ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_LSL_ADDR, lsl);
|
||||
ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_TL_ADDR, tl);
|
||||
ret |= i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR,
|
||||
mpr121->keycount);
|
||||
if (ret != 0)
|
||||
goto err_i2c_write;
|
||||
|
||||
dev_dbg(&client->dev, "set up with %x keys.\n", mpr121->keycount);
|
||||
|
||||
return 0;
|
||||
|
||||
err_i2c_write:
|
||||
dev_err(&client->dev, "i2c write error: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devinit mpr_touchkey_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
const struct mpr121_platform_data *pdata = client->dev.platform_data;
|
||||
struct mpr121_touchkey *mpr121;
|
||||
struct input_dev *input_dev;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&client->dev, "no platform data defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!pdata->keymap || !pdata->keymap_size) {
|
||||
dev_err(&client->dev, "missing keymap data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pdata->keymap_size > MPR121_MAX_KEY_COUNT) {
|
||||
dev_err(&client->dev, "too many keys defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!client->irq) {
|
||||
dev_err(&client->dev, "irq number should not be zero\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mpr121 = kzalloc(sizeof(struct mpr121_touchkey), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!mpr121 || !input_dev) {
|
||||
dev_err(&client->dev, "Failed to allocate memory\n");
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
mpr121->client = client;
|
||||
mpr121->input_dev = input_dev;
|
||||
mpr121->keycount = pdata->keymap_size;
|
||||
|
||||
input_dev->name = "Freescale MPR121 Touchkey";
|
||||
input_dev->id.bustype = BUS_I2C;
|
||||
input_dev->dev.parent = &client->dev;
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
|
||||
|
||||
input_dev->keycode = mpr121->keycodes;
|
||||
input_dev->keycodesize = sizeof(mpr121->keycodes[0]);
|
||||
input_dev->keycodemax = mpr121->keycount;
|
||||
|
||||
for (i = 0; i < pdata->keymap_size; i++) {
|
||||
input_set_capability(input_dev, EV_KEY, pdata->keymap[i]);
|
||||
mpr121->keycodes[i] = pdata->keymap[i];
|
||||
}
|
||||
|
||||
error = mpr121_phys_init(pdata, mpr121, client);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Failed to init register\n");
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
error = request_threaded_irq(client->irq, NULL,
|
||||
mpr_touchkey_interrupt,
|
||||
IRQF_TRIGGER_FALLING,
|
||||
client->dev.driver->name, mpr121);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Failed to register interrupt\n");
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
error = input_register_device(input_dev);
|
||||
if (error)
|
||||
goto err_free_irq;
|
||||
|
||||
i2c_set_clientdata(client, mpr121);
|
||||
device_init_wakeup(&client->dev, pdata->wakeup);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(client->irq, mpr121);
|
||||
err_free_mem:
|
||||
input_free_device(input_dev);
|
||||
kfree(mpr121);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit mpr_touchkey_remove(struct i2c_client *client)
|
||||
{
|
||||
struct mpr121_touchkey *mpr121 = i2c_get_clientdata(client);
|
||||
|
||||
free_irq(client->irq, mpr121);
|
||||
input_unregister_device(mpr121->input_dev);
|
||||
kfree(mpr121);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mpr_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
if (device_may_wakeup(&client->dev))
|
||||
enable_irq_wake(client->irq);
|
||||
|
||||
i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, 0x00);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpr_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct mpr121_touchkey *mpr121 = i2c_get_clientdata(client);
|
||||
|
||||
if (device_may_wakeup(&client->dev))
|
||||
disable_irq_wake(client->irq);
|
||||
|
||||
i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR,
|
||||
mpr121->keycount);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mpr121_touchkey_pm_ops, mpr_suspend, mpr_resume);
|
||||
|
||||
static const struct i2c_device_id mpr121_id[] = {
|
||||
{ "mpr121_touchkey", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mpr121_id);
|
||||
|
||||
static struct i2c_driver mpr_touchkey_driver = {
|
||||
.driver = {
|
||||
.name = "mpr121",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &mpr121_touchkey_pm_ops,
|
||||
},
|
||||
.id_table = mpr121_id,
|
||||
.probe = mpr_touchkey_probe,
|
||||
.remove = __devexit_p(mpr_touchkey_remove),
|
||||
};
|
||||
|
||||
static int __init mpr_touchkey_init(void)
|
||||
{
|
||||
return i2c_add_driver(&mpr_touchkey_driver);
|
||||
}
|
||||
module_init(mpr_touchkey_init);
|
||||
|
||||
static void __exit mpr_touchkey_exit(void)
|
||||
{
|
||||
i2c_del_driver(&mpr_touchkey_driver);
|
||||
}
|
||||
module_exit(mpr_touchkey_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Zhang Jiejing <jiejing.zhang@freescale.com>");
|
||||
MODULE_DESCRIPTION("Touch Key driver for Freescale MPR121 Chip");
|
@ -413,7 +413,7 @@ static int __devinit omap_kp_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
err5:
|
||||
for (i = irq_idx - 1; i >=0; i--)
|
||||
free_irq(row_gpios[i], NULL);
|
||||
free_irq(row_gpios[i], omap_kp);
|
||||
err4:
|
||||
input_unregister_device(omap_kp->input);
|
||||
input_dev = NULL;
|
||||
@ -444,11 +444,11 @@ static int __devexit omap_kp_remove(struct platform_device *pdev)
|
||||
gpio_free(col_gpios[i]);
|
||||
for (i = 0; i < omap_kp->rows; i++) {
|
||||
gpio_free(row_gpios[i]);
|
||||
free_irq(gpio_to_irq(row_gpios[i]), NULL);
|
||||
free_irq(gpio_to_irq(row_gpios[i]), omap_kp);
|
||||
}
|
||||
} else {
|
||||
omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
|
||||
free_irq(omap_kp->irq, NULL);
|
||||
free_irq(omap_kp->irq, omap_kp);
|
||||
}
|
||||
|
||||
del_timer_sync(&omap_kp->timer);
|
||||
|
@ -248,6 +248,7 @@ static const struct i2c_device_id qt1070_id[] = {
|
||||
{ "qt1070", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, qt1070_id);
|
||||
|
||||
static struct i2c_driver qt1070_driver = {
|
||||
.driver = {
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sh_keysc.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
@ -37,7 +37,6 @@ static const struct {
|
||||
|
||||
struct sh_keysc_priv {
|
||||
void __iomem *iomem_base;
|
||||
struct clk *clk;
|
||||
DECLARE_BITMAP(last_keys, SH_KEYSC_MAXKEYS);
|
||||
struct input_dev *input;
|
||||
struct sh_keysc_info pdata;
|
||||
@ -169,7 +168,6 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
|
||||
struct sh_keysc_info *pdata;
|
||||
struct resource *res;
|
||||
struct input_dev *input;
|
||||
char clk_name[8];
|
||||
int i;
|
||||
int irq, error;
|
||||
|
||||
@ -210,19 +208,11 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
|
||||
goto err1;
|
||||
}
|
||||
|
||||
snprintf(clk_name, sizeof(clk_name), "keysc%d", pdev->id);
|
||||
priv->clk = clk_get(&pdev->dev, clk_name);
|
||||
if (IS_ERR(priv->clk)) {
|
||||
dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
|
||||
error = PTR_ERR(priv->clk);
|
||||
goto err2;
|
||||
}
|
||||
|
||||
priv->input = input_allocate_device();
|
||||
if (!priv->input) {
|
||||
dev_err(&pdev->dev, "failed to allocate input device\n");
|
||||
error = -ENOMEM;
|
||||
goto err3;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
input = priv->input;
|
||||
@ -241,10 +231,11 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
|
||||
input->keycodesize = sizeof(pdata->keycodes[0]);
|
||||
input->keycodemax = ARRAY_SIZE(pdata->keycodes);
|
||||
|
||||
error = request_irq(irq, sh_keysc_isr, 0, pdev->name, pdev);
|
||||
error = request_threaded_irq(irq, NULL, sh_keysc_isr, IRQF_ONESHOT,
|
||||
dev_name(&pdev->dev), pdev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to request IRQ\n");
|
||||
goto err4;
|
||||
goto err3;
|
||||
}
|
||||
|
||||
for (i = 0; i < SH_KEYSC_MAXKEYS; i++)
|
||||
@ -254,10 +245,11 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to register input device\n");
|
||||
goto err5;
|
||||
goto err4;
|
||||
}
|
||||
|
||||
clk_enable(priv->clk);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
sh_keysc_write(priv, KYCR1, (sh_keysc_mode[pdata->mode].kymd << 8) |
|
||||
pdata->scan_timing);
|
||||
@ -267,12 +259,10 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
|
||||
|
||||
return 0;
|
||||
|
||||
err5:
|
||||
free_irq(irq, pdev);
|
||||
err4:
|
||||
input_free_device(input);
|
||||
free_irq(irq, pdev);
|
||||
err3:
|
||||
clk_put(priv->clk);
|
||||
input_free_device(input);
|
||||
err2:
|
||||
iounmap(priv->iomem_base);
|
||||
err1:
|
||||
@ -292,8 +282,8 @@ static int __devexit sh_keysc_remove(struct platform_device *pdev)
|
||||
free_irq(platform_get_irq(pdev, 0), pdev);
|
||||
iounmap(priv->iomem_base);
|
||||
|
||||
clk_disable(priv->clk);
|
||||
clk_put(priv->clk);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(priv);
|
||||
@ -301,6 +291,7 @@ static int __devexit sh_keysc_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_PM_SLEEP
|
||||
static int sh_keysc_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
@ -311,14 +302,13 @@ static int sh_keysc_suspend(struct device *dev)
|
||||
value = sh_keysc_read(priv, KYCR1);
|
||||
|
||||
if (device_may_wakeup(dev)) {
|
||||
value |= 0x80;
|
||||
sh_keysc_write(priv, KYCR1, value | 0x80);
|
||||
enable_irq_wake(irq);
|
||||
} else {
|
||||
value &= ~0x80;
|
||||
sh_keysc_write(priv, KYCR1, value & ~0x80);
|
||||
pm_runtime_put_sync(dev);
|
||||
}
|
||||
|
||||
sh_keysc_write(priv, KYCR1, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -329,16 +319,17 @@ static int sh_keysc_resume(struct device *dev)
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(irq);
|
||||
else
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops sh_keysc_dev_pm_ops = {
|
||||
.suspend = sh_keysc_suspend,
|
||||
.resume = sh_keysc_resume,
|
||||
};
|
||||
static SIMPLE_DEV_PM_OPS(sh_keysc_dev_pm_ops,
|
||||
sh_keysc_suspend, sh_keysc_resume);
|
||||
|
||||
struct platform_driver sh_keysc_device_driver = {
|
||||
static struct platform_driver sh_keysc_device_driver = {
|
||||
.probe = sh_keysc_probe,
|
||||
.remove = __devexit_p(sh_keysc_remove),
|
||||
.driver = {
|
||||
|
@ -66,12 +66,11 @@ struct tegra_kbc {
|
||||
void __iomem *mmio;
|
||||
struct input_dev *idev;
|
||||
unsigned int irq;
|
||||
unsigned int wake_enable_rows;
|
||||
unsigned int wake_enable_cols;
|
||||
spinlock_t lock;
|
||||
unsigned int repoll_dly;
|
||||
unsigned long cp_dly_jiffies;
|
||||
bool use_fn_map;
|
||||
bool use_ghost_filter;
|
||||
const struct tegra_kbc_platform_data *pdata;
|
||||
unsigned short keycode[KBC_MAX_KEY * 2];
|
||||
unsigned short current_keys[KBC_MAX_KPENT];
|
||||
@ -260,6 +259,8 @@ static void tegra_kbc_report_keys(struct tegra_kbc *kbc)
|
||||
unsigned int num_down = 0;
|
||||
unsigned long flags;
|
||||
bool fn_keypress = false;
|
||||
bool key_in_same_row = false;
|
||||
bool key_in_same_col = false;
|
||||
|
||||
spin_lock_irqsave(&kbc->lock, flags);
|
||||
for (i = 0; i < KBC_MAX_KPENT; i++) {
|
||||
@ -284,6 +285,34 @@ static void tegra_kbc_report_keys(struct tegra_kbc *kbc)
|
||||
val >>= 8;
|
||||
}
|
||||
|
||||
/*
|
||||
* Matrix keyboard designs are prone to keyboard ghosting.
|
||||
* Ghosting occurs if there are 3 keys such that -
|
||||
* any 2 of the 3 keys share a row, and any 2 of them share a column.
|
||||
* If so ignore the key presses for this iteration.
|
||||
*/
|
||||
if ((kbc->use_ghost_filter) && (num_down >= 3)) {
|
||||
for (i = 0; i < num_down; i++) {
|
||||
unsigned int j;
|
||||
u8 curr_col = scancodes[i] & 0x07;
|
||||
u8 curr_row = scancodes[i] >> KBC_ROW_SHIFT;
|
||||
|
||||
/*
|
||||
* Find 2 keys such that one key is in the same row
|
||||
* and the other is in the same column as the i-th key.
|
||||
*/
|
||||
for (j = i + 1; j < num_down; j++) {
|
||||
u8 col = scancodes[j] & 0x07;
|
||||
u8 row = scancodes[j] >> KBC_ROW_SHIFT;
|
||||
|
||||
if (col == curr_col)
|
||||
key_in_same_col = true;
|
||||
if (row == curr_row)
|
||||
key_in_same_row = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the platform uses Fn keymaps, translate keys on a Fn keypress.
|
||||
* Function keycodes are KBC_MAX_KEY apart from the plain keycodes.
|
||||
@ -297,6 +326,10 @@ static void tegra_kbc_report_keys(struct tegra_kbc *kbc)
|
||||
|
||||
spin_unlock_irqrestore(&kbc->lock, flags);
|
||||
|
||||
/* Ignore the key presses for this iteration? */
|
||||
if (key_in_same_col && key_in_same_row)
|
||||
return;
|
||||
|
||||
tegra_kbc_report_released_keys(kbc->idev,
|
||||
kbc->current_keys, kbc->num_pressed_keys,
|
||||
keycodes, num_down);
|
||||
@ -383,21 +416,11 @@ static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter)
|
||||
int i;
|
||||
unsigned int rst_val;
|
||||
|
||||
BUG_ON(pdata->wake_cnt > KBC_MAX_KEY);
|
||||
rst_val = (filter && pdata->wake_cnt) ? ~0 : 0;
|
||||
/* Either mask all keys or none. */
|
||||
rst_val = (filter && !pdata->wakeup) ? ~0 : 0;
|
||||
|
||||
for (i = 0; i < KBC_MAX_ROW; i++)
|
||||
writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4);
|
||||
|
||||
if (filter) {
|
||||
for (i = 0; i < pdata->wake_cnt; i++) {
|
||||
u32 val, addr;
|
||||
addr = pdata->wake_cfg[i].row * 4 + KBC_ROW0_MASK_0;
|
||||
val = readl(kbc->mmio + addr);
|
||||
val &= ~(1 << pdata->wake_cfg[i].col);
|
||||
writel(val, kbc->mmio + addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
|
||||
@ -559,7 +582,6 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev)
|
||||
struct resource *res;
|
||||
int irq;
|
||||
int err;
|
||||
int i;
|
||||
int num_rows = 0;
|
||||
unsigned int debounce_cnt;
|
||||
unsigned int scan_time_rows;
|
||||
@ -616,13 +638,6 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev)
|
||||
goto err_iounmap;
|
||||
}
|
||||
|
||||
kbc->wake_enable_rows = 0;
|
||||
kbc->wake_enable_cols = 0;
|
||||
for (i = 0; i < pdata->wake_cnt; i++) {
|
||||
kbc->wake_enable_rows |= (1 << pdata->wake_cfg[i].row);
|
||||
kbc->wake_enable_cols |= (1 << pdata->wake_cfg[i].col);
|
||||
}
|
||||
|
||||
/*
|
||||
* The time delay between two consecutive reads of the FIFO is
|
||||
* the sum of the repeat time and the time taken for scanning
|
||||
@ -652,6 +667,7 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev)
|
||||
input_dev->keycodemax *= 2;
|
||||
|
||||
kbc->use_fn_map = pdata->use_fn_map;
|
||||
kbc->use_ghost_filter = pdata->use_ghost_filter;
|
||||
keymap_data = pdata->keymap_data ?: &tegra_kbc_default_keymap_data;
|
||||
matrix_keypad_build_keymap(keymap_data, KBC_ROW_SHIFT,
|
||||
input_dev->keycode, input_dev->keybit);
|
||||
|
@ -79,13 +79,7 @@ struct ad714x_slider_drv {
|
||||
struct ad714x_wheel_drv {
|
||||
int abs_pos;
|
||||
int flt_pos;
|
||||
int pre_mean_value;
|
||||
int pre_highest_stage;
|
||||
int pre_mean_value_no_offset;
|
||||
int mean_value;
|
||||
int mean_value_no_offset;
|
||||
int pos_offset;
|
||||
int pos_ratio;
|
||||
int highest_stage;
|
||||
enum ad714x_device_state state;
|
||||
struct input_dev *input;
|
||||
@ -158,10 +152,10 @@ static void ad714x_use_com_int(struct ad714x_chip *ad714x,
|
||||
unsigned short data;
|
||||
unsigned short mask;
|
||||
|
||||
mask = ((1 << (end_stage + 1)) - 1) - (1 << start_stage);
|
||||
mask = ((1 << (end_stage + 1)) - 1) - ((1 << start_stage) - 1);
|
||||
|
||||
ad714x->read(ad714x->dev, STG_COM_INT_EN_REG, &data);
|
||||
data |= 1 << start_stage;
|
||||
data |= 1 << end_stage;
|
||||
ad714x->write(ad714x->dev, STG_COM_INT_EN_REG, data);
|
||||
|
||||
ad714x->read(ad714x->dev, STG_HIGH_INT_EN_REG, &data);
|
||||
@ -175,10 +169,10 @@ static void ad714x_use_thr_int(struct ad714x_chip *ad714x,
|
||||
unsigned short data;
|
||||
unsigned short mask;
|
||||
|
||||
mask = ((1 << (end_stage + 1)) - 1) - (1 << start_stage);
|
||||
mask = ((1 << (end_stage + 1)) - 1) - ((1 << start_stage) - 1);
|
||||
|
||||
ad714x->read(ad714x->dev, STG_COM_INT_EN_REG, &data);
|
||||
data &= ~(1 << start_stage);
|
||||
data &= ~(1 << end_stage);
|
||||
ad714x->write(ad714x->dev, STG_COM_INT_EN_REG, data);
|
||||
|
||||
ad714x->read(ad714x->dev, STG_HIGH_INT_EN_REG, &data);
|
||||
@ -404,7 +398,6 @@ static void ad714x_slider_state_machine(struct ad714x_chip *ad714x, int idx)
|
||||
ad714x_slider_cal_highest_stage(ad714x, idx);
|
||||
ad714x_slider_cal_abs_pos(ad714x, idx);
|
||||
ad714x_slider_cal_flt_pos(ad714x, idx);
|
||||
|
||||
input_report_abs(sw->input, ABS_X, sw->flt_pos);
|
||||
input_report_key(sw->input, BTN_TOUCH, 1);
|
||||
} else {
|
||||
@ -468,104 +461,41 @@ static void ad714x_wheel_cal_sensor_val(struct ad714x_chip *ad714x, int idx)
|
||||
/*
|
||||
* When the scroll wheel is activated, we compute the absolute position based
|
||||
* on the sensor values. To calculate the position, we first determine the
|
||||
* sensor that has the greatest response among the 8 sensors that constitutes
|
||||
* the scrollwheel. Then we determined the 2 sensors on either sides of the
|
||||
* sensor that has the greatest response among the sensors that constitutes
|
||||
* the scrollwheel. Then we determined the sensors on either sides of the
|
||||
* sensor with the highest response and we apply weights to these sensors. The
|
||||
* result of this computation gives us the mean value which defined by the
|
||||
* following formula:
|
||||
* For i= second_before_highest_stage to i= second_after_highest_stage
|
||||
* v += Sensor response(i)*WEIGHT*(i+3)
|
||||
* w += Sensor response(i)
|
||||
* Mean_Value=v/w
|
||||
* pos_on_scrollwheel = (Mean_Value - position_offset) / position_ratio
|
||||
* result of this computation gives us the mean value.
|
||||
*/
|
||||
|
||||
#define WEIGHT_FACTOR 30
|
||||
/* This constant prevents the "PositionOffset" from reaching a big value */
|
||||
#define OFFSET_POSITION_CLAMP 120
|
||||
static void ad714x_wheel_cal_abs_pos(struct ad714x_chip *ad714x, int idx)
|
||||
{
|
||||
struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
|
||||
struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx];
|
||||
int stage_num = hw->end_stage - hw->start_stage + 1;
|
||||
int second_before, first_before, highest, first_after, second_after;
|
||||
int first_before, highest, first_after;
|
||||
int a_param, b_param;
|
||||
|
||||
/* Calculate Mean value */
|
||||
|
||||
second_before = (sw->highest_stage + stage_num - 2) % stage_num;
|
||||
first_before = (sw->highest_stage + stage_num - 1) % stage_num;
|
||||
highest = sw->highest_stage;
|
||||
first_after = (sw->highest_stage + stage_num + 1) % stage_num;
|
||||
second_after = (sw->highest_stage + stage_num + 2) % stage_num;
|
||||
|
||||
if (((sw->highest_stage - hw->start_stage) > 1) &&
|
||||
((hw->end_stage - sw->highest_stage) > 1)) {
|
||||
a_param = ad714x->sensor_val[second_before] *
|
||||
(second_before - hw->start_stage + 3) +
|
||||
ad714x->sensor_val[first_before] *
|
||||
(second_before - hw->start_stage + 3) +
|
||||
ad714x->sensor_val[highest] *
|
||||
(second_before - hw->start_stage + 3) +
|
||||
ad714x->sensor_val[first_after] *
|
||||
(first_after - hw->start_stage + 3) +
|
||||
ad714x->sensor_val[second_after] *
|
||||
(second_after - hw->start_stage + 3);
|
||||
} else {
|
||||
a_param = ad714x->sensor_val[second_before] *
|
||||
(second_before - hw->start_stage + 1) +
|
||||
ad714x->sensor_val[first_before] *
|
||||
(second_before - hw->start_stage + 2) +
|
||||
ad714x->sensor_val[highest] *
|
||||
(second_before - hw->start_stage + 3) +
|
||||
ad714x->sensor_val[first_after] *
|
||||
(first_after - hw->start_stage + 4) +
|
||||
ad714x->sensor_val[second_after] *
|
||||
(second_after - hw->start_stage + 5);
|
||||
}
|
||||
a_param *= WEIGHT_FACTOR;
|
||||
|
||||
b_param = ad714x->sensor_val[second_before] +
|
||||
a_param = ad714x->sensor_val[highest] *
|
||||
(highest - hw->start_stage) +
|
||||
ad714x->sensor_val[first_before] *
|
||||
(highest - hw->start_stage - 1) +
|
||||
ad714x->sensor_val[first_after] *
|
||||
(highest - hw->start_stage + 1);
|
||||
b_param = ad714x->sensor_val[highest] +
|
||||
ad714x->sensor_val[first_before] +
|
||||
ad714x->sensor_val[highest] +
|
||||
ad714x->sensor_val[first_after] +
|
||||
ad714x->sensor_val[second_after];
|
||||
ad714x->sensor_val[first_after];
|
||||
|
||||
sw->pre_mean_value = sw->mean_value;
|
||||
sw->mean_value = a_param / b_param;
|
||||
sw->abs_pos = ((hw->max_coord / (hw->end_stage - hw->start_stage)) *
|
||||
a_param) / b_param;
|
||||
|
||||
/* Calculate the offset */
|
||||
|
||||
if ((sw->pre_highest_stage == hw->end_stage) &&
|
||||
(sw->highest_stage == hw->start_stage))
|
||||
sw->pos_offset = sw->mean_value;
|
||||
else if ((sw->pre_highest_stage == hw->start_stage) &&
|
||||
(sw->highest_stage == hw->end_stage))
|
||||
sw->pos_offset = sw->pre_mean_value;
|
||||
|
||||
if (sw->pos_offset > OFFSET_POSITION_CLAMP)
|
||||
sw->pos_offset = OFFSET_POSITION_CLAMP;
|
||||
|
||||
/* Calculate the mean value without the offset */
|
||||
|
||||
sw->pre_mean_value_no_offset = sw->mean_value_no_offset;
|
||||
sw->mean_value_no_offset = sw->mean_value - sw->pos_offset;
|
||||
if (sw->mean_value_no_offset < 0)
|
||||
sw->mean_value_no_offset = 0;
|
||||
|
||||
/* Calculate ratio to scale down to NUMBER_OF_WANTED_POSITIONS */
|
||||
|
||||
if ((sw->pre_highest_stage == hw->end_stage) &&
|
||||
(sw->highest_stage == hw->start_stage))
|
||||
sw->pos_ratio = (sw->pre_mean_value_no_offset * 100) /
|
||||
hw->max_coord;
|
||||
else if ((sw->pre_highest_stage == hw->start_stage) &&
|
||||
(sw->highest_stage == hw->end_stage))
|
||||
sw->pos_ratio = (sw->mean_value_no_offset * 100) /
|
||||
hw->max_coord;
|
||||
sw->abs_pos = (sw->mean_value_no_offset * 100) / sw->pos_ratio;
|
||||
if (sw->abs_pos > hw->max_coord)
|
||||
sw->abs_pos = hw->max_coord;
|
||||
else if (sw->abs_pos < 0)
|
||||
sw->abs_pos = 0;
|
||||
}
|
||||
|
||||
static void ad714x_wheel_cal_flt_pos(struct ad714x_chip *ad714x, int idx)
|
||||
@ -639,9 +569,8 @@ static void ad714x_wheel_state_machine(struct ad714x_chip *ad714x, int idx)
|
||||
ad714x_wheel_cal_highest_stage(ad714x, idx);
|
||||
ad714x_wheel_cal_abs_pos(ad714x, idx);
|
||||
ad714x_wheel_cal_flt_pos(ad714x, idx);
|
||||
|
||||
input_report_abs(sw->input, ABS_WHEEL,
|
||||
sw->abs_pos);
|
||||
sw->flt_pos);
|
||||
input_report_key(sw->input, BTN_TOUCH, 1);
|
||||
} else {
|
||||
/* When the user lifts off the sensor, configure
|
||||
@ -1149,6 +1078,8 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
|
||||
input[alloc_idx]->id.bustype = bus_type;
|
||||
input[alloc_idx]->id.product = ad714x->product;
|
||||
input[alloc_idx]->id.version = ad714x->version;
|
||||
input[alloc_idx]->name = "ad714x_captouch_slider";
|
||||
input[alloc_idx]->dev.parent = dev;
|
||||
|
||||
error = input_register_device(input[alloc_idx]);
|
||||
if (error)
|
||||
@ -1179,6 +1110,8 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
|
||||
input[alloc_idx]->id.bustype = bus_type;
|
||||
input[alloc_idx]->id.product = ad714x->product;
|
||||
input[alloc_idx]->id.version = ad714x->version;
|
||||
input[alloc_idx]->name = "ad714x_captouch_wheel";
|
||||
input[alloc_idx]->dev.parent = dev;
|
||||
|
||||
error = input_register_device(input[alloc_idx]);
|
||||
if (error)
|
||||
@ -1212,6 +1145,8 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
|
||||
input[alloc_idx]->id.bustype = bus_type;
|
||||
input[alloc_idx]->id.product = ad714x->product;
|
||||
input[alloc_idx]->id.version = ad714x->version;
|
||||
input[alloc_idx]->name = "ad714x_captouch_pad";
|
||||
input[alloc_idx]->dev.parent = dev;
|
||||
|
||||
error = input_register_device(input[alloc_idx]);
|
||||
if (error)
|
||||
@ -1240,6 +1175,8 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
|
||||
input[alloc_idx]->id.bustype = bus_type;
|
||||
input[alloc_idx]->id.product = ad714x->product;
|
||||
input[alloc_idx]->id.version = ad714x->version;
|
||||
input[alloc_idx]->name = "ad714x_captouch_button";
|
||||
input[alloc_idx]->dev.parent = dev;
|
||||
|
||||
error = input_register_device(input[alloc_idx]);
|
||||
if (error)
|
||||
@ -1249,7 +1186,9 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
|
||||
}
|
||||
|
||||
error = request_threaded_irq(ad714x->irq, NULL, ad714x_interrupt_thread,
|
||||
IRQF_TRIGGER_FALLING, "ad714x_captouch", ad714x);
|
||||
plat_data->irqflags ?
|
||||
plat_data->irqflags : IRQF_TRIGGER_FALLING,
|
||||
"ad714x_captouch", ad714x);
|
||||
if (error) {
|
||||
dev_err(dev, "can't allocate irq %d\n", ad714x->irq);
|
||||
goto err_unreg_dev;
|
||||
|
@ -737,14 +737,17 @@ static ssize_t ati_remote2_store_channel_mask(struct device *dev,
|
||||
|
||||
mutex_lock(&ati_remote2_mutex);
|
||||
|
||||
if (mask != ar2->channel_mask && !ati_remote2_setup(ar2, mask))
|
||||
ar2->channel_mask = mask;
|
||||
if (mask != ar2->channel_mask) {
|
||||
r = ati_remote2_setup(ar2, mask);
|
||||
if (!r)
|
||||
ar2->channel_mask = mask;
|
||||
}
|
||||
|
||||
mutex_unlock(&ati_remote2_mutex);
|
||||
|
||||
usb_autopm_put_interface(ar2->intf[0]);
|
||||
|
||||
return count;
|
||||
return r ? r : count;
|
||||
}
|
||||
|
||||
static ssize_t ati_remote2_show_mode_mask(struct device *dev,
|
||||
|
@ -2,6 +2,7 @@
|
||||
* rotary_encoder.c
|
||||
*
|
||||
* (c) 2009 Daniel Mack <daniel@caiaq.de>
|
||||
* Copyright (C) 2011 Johan Hovold <jhovold@gmail.com>
|
||||
*
|
||||
* state machine code inspired by code from Tim Ruetz
|
||||
*
|
||||
@ -38,52 +39,66 @@ struct rotary_encoder {
|
||||
|
||||
bool armed;
|
||||
unsigned char dir; /* 0 - clockwise, 1 - CCW */
|
||||
|
||||
char last_stable;
|
||||
};
|
||||
|
||||
static int rotary_encoder_get_state(struct rotary_encoder_platform_data *pdata)
|
||||
{
|
||||
int a = !!gpio_get_value(pdata->gpio_a);
|
||||
int b = !!gpio_get_value(pdata->gpio_b);
|
||||
|
||||
a ^= pdata->inverted_a;
|
||||
b ^= pdata->inverted_b;
|
||||
|
||||
return ((a << 1) | b);
|
||||
}
|
||||
|
||||
static void rotary_encoder_report_event(struct rotary_encoder *encoder)
|
||||
{
|
||||
struct rotary_encoder_platform_data *pdata = encoder->pdata;
|
||||
|
||||
if (pdata->relative_axis) {
|
||||
input_report_rel(encoder->input,
|
||||
pdata->axis, encoder->dir ? -1 : 1);
|
||||
} else {
|
||||
unsigned int pos = encoder->pos;
|
||||
|
||||
if (encoder->dir) {
|
||||
/* turning counter-clockwise */
|
||||
if (pdata->rollover)
|
||||
pos += pdata->steps;
|
||||
if (pos)
|
||||
pos--;
|
||||
} else {
|
||||
/* turning clockwise */
|
||||
if (pdata->rollover || pos < pdata->steps)
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (pdata->rollover)
|
||||
pos %= pdata->steps;
|
||||
|
||||
encoder->pos = pos;
|
||||
input_report_abs(encoder->input, pdata->axis, encoder->pos);
|
||||
}
|
||||
|
||||
input_sync(encoder->input);
|
||||
}
|
||||
|
||||
static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct rotary_encoder *encoder = dev_id;
|
||||
struct rotary_encoder_platform_data *pdata = encoder->pdata;
|
||||
int a = !!gpio_get_value(pdata->gpio_a);
|
||||
int b = !!gpio_get_value(pdata->gpio_b);
|
||||
int state;
|
||||
|
||||
a ^= pdata->inverted_a;
|
||||
b ^= pdata->inverted_b;
|
||||
state = (a << 1) | b;
|
||||
state = rotary_encoder_get_state(encoder->pdata);
|
||||
|
||||
switch (state) {
|
||||
|
||||
case 0x0:
|
||||
if (!encoder->armed)
|
||||
break;
|
||||
|
||||
if (pdata->relative_axis) {
|
||||
input_report_rel(encoder->input, pdata->axis,
|
||||
encoder->dir ? -1 : 1);
|
||||
} else {
|
||||
unsigned int pos = encoder->pos;
|
||||
|
||||
if (encoder->dir) {
|
||||
/* turning counter-clockwise */
|
||||
if (pdata->rollover)
|
||||
pos += pdata->steps;
|
||||
if (pos)
|
||||
pos--;
|
||||
} else {
|
||||
/* turning clockwise */
|
||||
if (pdata->rollover || pos < pdata->steps)
|
||||
pos++;
|
||||
}
|
||||
if (pdata->rollover)
|
||||
pos %= pdata->steps;
|
||||
encoder->pos = pos;
|
||||
input_report_abs(encoder->input, pdata->axis,
|
||||
encoder->pos);
|
||||
if (encoder->armed) {
|
||||
rotary_encoder_report_event(encoder);
|
||||
encoder->armed = false;
|
||||
}
|
||||
input_sync(encoder->input);
|
||||
|
||||
encoder->armed = false;
|
||||
break;
|
||||
|
||||
case 0x1:
|
||||
@ -100,11 +115,37 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct rotary_encoder *encoder = dev_id;
|
||||
int state;
|
||||
|
||||
state = rotary_encoder_get_state(encoder->pdata);
|
||||
|
||||
switch (state) {
|
||||
case 0x00:
|
||||
case 0x03:
|
||||
if (state != encoder->last_stable) {
|
||||
rotary_encoder_report_event(encoder);
|
||||
encoder->last_stable = state;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
encoder->dir = (encoder->last_stable + state) & 0x01;
|
||||
break;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit rotary_encoder_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct rotary_encoder *encoder;
|
||||
struct input_dev *input;
|
||||
irq_handler_t handler;
|
||||
int err;
|
||||
|
||||
if (!pdata) {
|
||||
@ -175,7 +216,14 @@ static int __devinit rotary_encoder_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* request the IRQs */
|
||||
err = request_irq(encoder->irq_a, &rotary_encoder_irq,
|
||||
if (pdata->half_period) {
|
||||
handler = &rotary_encoder_half_period_irq;
|
||||
encoder->last_stable = rotary_encoder_get_state(pdata);
|
||||
} else {
|
||||
handler = &rotary_encoder_irq;
|
||||
}
|
||||
|
||||
err = request_irq(encoder->irq_a, handler,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||
DRV_NAME, encoder);
|
||||
if (err) {
|
||||
@ -184,7 +232,7 @@ static int __devinit rotary_encoder_probe(struct platform_device *pdev)
|
||||
goto exit_free_gpio_b;
|
||||
}
|
||||
|
||||
err = request_irq(encoder->irq_b, &rotary_encoder_irq,
|
||||
err = request_irq(encoder->irq_b, handler,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||
DRV_NAME, encoder);
|
||||
if (err) {
|
||||
@ -252,6 +300,5 @@ module_exit(rotary_encoder_exit);
|
||||
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DESCRIPTION("GPIO rotary encoder driver");
|
||||
MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
|
||||
MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>, Johan Hovold");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
|
@ -89,7 +89,7 @@ static int __init twl4030_pwrbutton_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
free_irq:
|
||||
free_irq(irq, NULL);
|
||||
free_irq(irq, pwr);
|
||||
free_input_dev:
|
||||
input_free_device(pwr);
|
||||
return err;
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/serio.h>
|
||||
#include <linux/libps2.h>
|
||||
#include "psmouse.h"
|
||||
@ -242,15 +243,37 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
static void elantech_set_slot(struct input_dev *dev, int slot, bool active,
|
||||
unsigned int x, unsigned int y)
|
||||
{
|
||||
input_mt_slot(dev, slot);
|
||||
input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
|
||||
if (active) {
|
||||
input_report_abs(dev, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(dev, ABS_MT_POSITION_Y, y);
|
||||
}
|
||||
}
|
||||
|
||||
/* x1 < x2 and y1 < y2 when two fingers, x = y = 0 when not pressed */
|
||||
static void elantech_report_semi_mt_data(struct input_dev *dev,
|
||||
unsigned int num_fingers,
|
||||
unsigned int x1, unsigned int y1,
|
||||
unsigned int x2, unsigned int y2)
|
||||
{
|
||||
elantech_set_slot(dev, 0, num_fingers != 0, x1, y1);
|
||||
elantech_set_slot(dev, 1, num_fingers == 2, x2, y2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Interpret complete data packets and report absolute mode input events for
|
||||
* hardware version 2. (6 byte packets)
|
||||
*/
|
||||
static void elantech_report_absolute_v2(struct psmouse *psmouse)
|
||||
{
|
||||
struct elantech_data *etd = psmouse->private;
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
unsigned char *packet = psmouse->packet;
|
||||
int fingers, x1, y1, x2, y2;
|
||||
unsigned int fingers, x1 = 0, y1 = 0, x2 = 0, y2 = 0, width = 0, pres = 0;
|
||||
|
||||
/* byte 0: n1 n0 . . . . R L */
|
||||
fingers = (packet[0] & 0xc0) >> 6;
|
||||
@ -270,14 +293,18 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
|
||||
* byte 1: . . . . . x10 x9 x8
|
||||
* byte 2: x7 x6 x5 x4 x4 x2 x1 x0
|
||||
*/
|
||||
input_report_abs(dev, ABS_X,
|
||||
((packet[1] & 0x07) << 8) | packet[2]);
|
||||
x1 = ((packet[1] & 0x07) << 8) | packet[2];
|
||||
/*
|
||||
* byte 4: . . . . . . y9 y8
|
||||
* byte 5: y7 y6 y5 y4 y3 y2 y1 y0
|
||||
*/
|
||||
input_report_abs(dev, ABS_Y,
|
||||
ETP_YMAX_V2 - (((packet[4] & 0x03) << 8) | packet[5]));
|
||||
y1 = ETP_YMAX_V2 - (((packet[4] & 0x03) << 8) | packet[5]);
|
||||
|
||||
input_report_abs(dev, ABS_X, x1);
|
||||
input_report_abs(dev, ABS_Y, y1);
|
||||
|
||||
pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4);
|
||||
width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
@ -303,23 +330,24 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
|
||||
*/
|
||||
input_report_abs(dev, ABS_X, x1 << 2);
|
||||
input_report_abs(dev, ABS_Y, y1 << 2);
|
||||
/*
|
||||
* For compatibility with the proprietary X Elantech driver
|
||||
* report both coordinates as hat coordinates
|
||||
*/
|
||||
input_report_abs(dev, ABS_HAT0X, x1);
|
||||
input_report_abs(dev, ABS_HAT0Y, y1);
|
||||
input_report_abs(dev, ABS_HAT1X, x2);
|
||||
input_report_abs(dev, ABS_HAT1Y, y2);
|
||||
|
||||
/* Unknown so just report sensible values */
|
||||
pres = 127;
|
||||
width = 7;
|
||||
break;
|
||||
}
|
||||
|
||||
elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
|
||||
input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
|
||||
input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
|
||||
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
|
||||
input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4);
|
||||
input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
|
||||
input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
|
||||
if (etd->reports_pressure) {
|
||||
input_report_abs(dev, ABS_PRESSURE, pres);
|
||||
input_report_abs(dev, ABS_TOOL_WIDTH, width);
|
||||
}
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
@ -478,10 +506,16 @@ static void elantech_set_input_params(struct psmouse *psmouse)
|
||||
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
|
||||
input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0);
|
||||
input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0);
|
||||
input_set_abs_params(dev, ABS_HAT0X, ETP_2FT_XMIN, ETP_2FT_XMAX, 0, 0);
|
||||
input_set_abs_params(dev, ABS_HAT0Y, ETP_2FT_YMIN, ETP_2FT_YMAX, 0, 0);
|
||||
input_set_abs_params(dev, ABS_HAT1X, ETP_2FT_XMIN, ETP_2FT_XMAX, 0, 0);
|
||||
input_set_abs_params(dev, ABS_HAT1Y, ETP_2FT_YMIN, ETP_2FT_YMAX, 0, 0);
|
||||
if (etd->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,
|
||||
ETP_WMAX_V2, 0, 0);
|
||||
}
|
||||
__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
|
||||
input_mt_init_slots(dev, 2);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -725,6 +759,10 @@ int elantech_init(struct psmouse *psmouse)
|
||||
etd->debug = 1;
|
||||
/* Don't know how to do parity checking for version 2 */
|
||||
etd->paritycheck = 0;
|
||||
|
||||
if (etd->fw_version >= 0x020800)
|
||||
etd->reports_pressure = true;
|
||||
|
||||
} else {
|
||||
etd->hw_version = 1;
|
||||
etd->paritycheck = 1;
|
||||
|
@ -77,6 +77,11 @@
|
||||
#define ETP_YMIN_V2 ( 0 + ETP_EDGE_FUZZ_V2)
|
||||
#define ETP_YMAX_V2 ( 768 - ETP_EDGE_FUZZ_V2)
|
||||
|
||||
#define ETP_PMIN_V2 0
|
||||
#define ETP_PMAX_V2 255
|
||||
#define ETP_WMIN_V2 0
|
||||
#define ETP_WMAX_V2 15
|
||||
|
||||
/*
|
||||
* For two finger touches the coordinate of each finger gets reported
|
||||
* separately but with reduced resolution.
|
||||
@ -102,6 +107,7 @@ struct elantech_data {
|
||||
unsigned char capabilities;
|
||||
bool paritycheck;
|
||||
bool jumpy_cursor;
|
||||
bool reports_pressure;
|
||||
unsigned char hw_version;
|
||||
unsigned int fw_version;
|
||||
unsigned int single_finger_reports;
|
||||
|
@ -508,7 +508,6 @@ static void mousedev_attach_client(struct mousedev *mousedev,
|
||||
spin_lock(&mousedev->client_lock);
|
||||
list_add_tail_rcu(&client->node, &mousedev->client_list);
|
||||
spin_unlock(&mousedev->client_lock);
|
||||
synchronize_rcu();
|
||||
}
|
||||
|
||||
static void mousedev_detach_client(struct mousedev *mousedev,
|
||||
|
@ -248,6 +248,18 @@ config TOUCHSCREEN_LPC32XX
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called lpc32xx_ts.
|
||||
|
||||
config TOUCHSCREEN_MAX11801
|
||||
tristate "MAX11801 based touchscreens"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you have a MAX11801 based touchscreen
|
||||
controller.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called max11801_ts.
|
||||
|
||||
config TOUCHSCREEN_MCS5000
|
||||
tristate "MELFAS MCS-5000 touchscreen"
|
||||
depends on I2C
|
||||
|
@ -27,6 +27,7 @@ obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o
|
||||
|
@ -109,6 +109,7 @@ struct ads7846 {
|
||||
u16 pressure_max;
|
||||
|
||||
bool swap_xy;
|
||||
bool use_internal;
|
||||
|
||||
struct ads7846_packet *packet;
|
||||
|
||||
@ -307,7 +308,6 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
|
||||
struct ads7846 *ts = dev_get_drvdata(dev);
|
||||
struct ser_req *req;
|
||||
int status;
|
||||
int use_internal;
|
||||
|
||||
req = kzalloc(sizeof *req, GFP_KERNEL);
|
||||
if (!req)
|
||||
@ -315,11 +315,8 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
|
||||
|
||||
spi_message_init(&req->msg);
|
||||
|
||||
/* FIXME boards with ads7846 might use external vref instead ... */
|
||||
use_internal = (ts->model == 7846);
|
||||
|
||||
/* maybe turn on internal vREF, and let it settle */
|
||||
if (use_internal) {
|
||||
if (ts->use_internal) {
|
||||
req->ref_on = REF_ON;
|
||||
req->xfer[0].tx_buf = &req->ref_on;
|
||||
req->xfer[0].len = 1;
|
||||
@ -331,8 +328,14 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
|
||||
/* for 1uF, settle for 800 usec; no cap, 100 usec. */
|
||||
req->xfer[1].delay_usecs = ts->vref_delay_usecs;
|
||||
spi_message_add_tail(&req->xfer[1], &req->msg);
|
||||
|
||||
/* Enable reference voltage */
|
||||
command |= ADS_PD10_REF_ON;
|
||||
}
|
||||
|
||||
/* Enable ADC in every case */
|
||||
command |= ADS_PD10_ADC_ON;
|
||||
|
||||
/* take sample */
|
||||
req->command = (u8) command;
|
||||
req->xfer[2].tx_buf = &req->command;
|
||||
@ -416,7 +419,7 @@ name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct ads7846 *ts = dev_get_drvdata(dev); \
|
||||
ssize_t v = ads7846_read12_ser(dev, \
|
||||
READ_12BIT_SER(var) | ADS_PD10_ALL_ON); \
|
||||
READ_12BIT_SER(var)); \
|
||||
if (v < 0) \
|
||||
return v; \
|
||||
return sprintf(buf, "%u\n", adjust(ts, v)); \
|
||||
@ -509,6 +512,7 @@ static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts)
|
||||
if (!ts->vref_mv) {
|
||||
dev_dbg(&spi->dev, "assuming 2.5V internal vREF\n");
|
||||
ts->vref_mv = 2500;
|
||||
ts->use_internal = true;
|
||||
}
|
||||
break;
|
||||
case 7845:
|
||||
@ -969,6 +973,13 @@ static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads784
|
||||
pdata->gpio_pendown);
|
||||
return err;
|
||||
}
|
||||
err = gpio_direction_input(pdata->gpio_pendown);
|
||||
if (err) {
|
||||
dev_err(&spi->dev, "failed to setup pendown GPIO%d\n",
|
||||
pdata->gpio_pendown);
|
||||
gpio_free(pdata->gpio_pendown);
|
||||
return err;
|
||||
}
|
||||
|
||||
ts->gpio_pendown = pdata->gpio_pendown;
|
||||
|
||||
@ -1340,8 +1351,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
||||
if (ts->model == 7845)
|
||||
ads7845_read12_ser(&spi->dev, PWRDOWN);
|
||||
else
|
||||
(void) ads7846_read12_ser(&spi->dev,
|
||||
READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON);
|
||||
(void) ads7846_read12_ser(&spi->dev, READ_12BIT_SER(vaux));
|
||||
|
||||
err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group);
|
||||
if (err)
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/atmel_mxt_ts.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
@ -196,9 +196,12 @@
|
||||
#define MXT_PRESS (1 << 6)
|
||||
#define MXT_DETECT (1 << 7)
|
||||
|
||||
/* Touch orient bits */
|
||||
#define MXT_XY_SWITCH (1 << 0)
|
||||
#define MXT_X_INVERT (1 << 1)
|
||||
#define MXT_Y_INVERT (1 << 2)
|
||||
|
||||
/* Touchscreen absolute values */
|
||||
#define MXT_MAX_XC 0x3ff
|
||||
#define MXT_MAX_YC 0x3ff
|
||||
#define MXT_MAX_AREA 0xff
|
||||
|
||||
#define MXT_MAX_FINGER 10
|
||||
@ -246,6 +249,8 @@ struct mxt_data {
|
||||
struct mxt_info info;
|
||||
struct mxt_finger finger[MXT_MAX_FINGER];
|
||||
unsigned int irq;
|
||||
unsigned int max_x;
|
||||
unsigned int max_y;
|
||||
};
|
||||
|
||||
static bool mxt_object_readable(unsigned int type)
|
||||
@ -499,19 +504,21 @@ static void mxt_input_report(struct mxt_data *data, int single_id)
|
||||
if (!finger[id].status)
|
||||
continue;
|
||||
|
||||
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
|
||||
finger[id].status != MXT_RELEASE ?
|
||||
finger[id].area : 0);
|
||||
input_report_abs(input_dev, ABS_MT_POSITION_X,
|
||||
finger[id].x);
|
||||
input_report_abs(input_dev, ABS_MT_POSITION_Y,
|
||||
finger[id].y);
|
||||
input_mt_sync(input_dev);
|
||||
input_mt_slot(input_dev, id);
|
||||
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER,
|
||||
finger[id].status != MXT_RELEASE);
|
||||
|
||||
if (finger[id].status == MXT_RELEASE)
|
||||
finger[id].status = 0;
|
||||
else
|
||||
if (finger[id].status != MXT_RELEASE) {
|
||||
finger_num++;
|
||||
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
|
||||
finger[id].area);
|
||||
input_report_abs(input_dev, ABS_MT_POSITION_X,
|
||||
finger[id].x);
|
||||
input_report_abs(input_dev, ABS_MT_POSITION_Y,
|
||||
finger[id].y);
|
||||
} else {
|
||||
finger[id].status = 0;
|
||||
}
|
||||
}
|
||||
|
||||
input_report_key(input_dev, BTN_TOUCH, finger_num > 0);
|
||||
@ -549,8 +556,13 @@ static void mxt_input_touchevent(struct mxt_data *data,
|
||||
if (!(status & (MXT_PRESS | MXT_MOVE)))
|
||||
return;
|
||||
|
||||
x = (message->message[1] << 2) | ((message->message[3] & ~0x3f) >> 6);
|
||||
y = (message->message[2] << 2) | ((message->message[3] & ~0xf3) >> 2);
|
||||
x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf);
|
||||
y = (message->message[2] << 4) | ((message->message[3] & 0xf));
|
||||
if (data->max_x < 1024)
|
||||
x = x >> 2;
|
||||
if (data->max_y < 1024)
|
||||
y = y >> 2;
|
||||
|
||||
area = message->message[4];
|
||||
|
||||
dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id,
|
||||
@ -804,10 +816,6 @@ static int mxt_initialize(struct mxt_data *data)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = mxt_make_highchg(data);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
mxt_handle_pdata(data);
|
||||
|
||||
/* Backup to memory */
|
||||
@ -845,6 +853,20 @@ static int mxt_initialize(struct mxt_data *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mxt_calc_resolution(struct mxt_data *data)
|
||||
{
|
||||
unsigned int max_x = data->pdata->x_size - 1;
|
||||
unsigned int max_y = data->pdata->y_size - 1;
|
||||
|
||||
if (data->pdata->orient & MXT_XY_SWITCH) {
|
||||
data->max_x = max_y;
|
||||
data->max_y = max_x;
|
||||
} else {
|
||||
data->max_x = max_x;
|
||||
data->max_y = max_y;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t mxt_object_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
@ -981,6 +1003,10 @@ static ssize_t mxt_update_fw_store(struct device *dev,
|
||||
|
||||
enable_irq(data->irq);
|
||||
|
||||
error = mxt_make_highchg(data);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -1052,31 +1078,33 @@ static int __devinit mxt_probe(struct i2c_client *client,
|
||||
input_dev->open = mxt_input_open;
|
||||
input_dev->close = mxt_input_close;
|
||||
|
||||
data->client = client;
|
||||
data->input_dev = input_dev;
|
||||
data->pdata = pdata;
|
||||
data->irq = client->irq;
|
||||
|
||||
mxt_calc_resolution(data);
|
||||
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
|
||||
/* For single touch */
|
||||
input_set_abs_params(input_dev, ABS_X,
|
||||
0, MXT_MAX_XC, 0, 0);
|
||||
0, data->max_x, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y,
|
||||
0, MXT_MAX_YC, 0, 0);
|
||||
0, data->max_y, 0, 0);
|
||||
|
||||
/* For multi touch */
|
||||
input_mt_init_slots(input_dev, MXT_MAX_FINGER);
|
||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
|
||||
0, MXT_MAX_AREA, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
|
||||
0, MXT_MAX_XC, 0, 0);
|
||||
0, data->max_x, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
|
||||
0, MXT_MAX_YC, 0, 0);
|
||||
0, data->max_y, 0, 0);
|
||||
|
||||
input_set_drvdata(input_dev, data);
|
||||
|
||||
data->client = client;
|
||||
data->input_dev = input_dev;
|
||||
data->pdata = pdata;
|
||||
data->irq = client->irq;
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
|
||||
error = mxt_initialize(data);
|
||||
@ -1090,6 +1118,10 @@ static int __devinit mxt_probe(struct i2c_client *client,
|
||||
goto err_free_object;
|
||||
}
|
||||
|
||||
error = mxt_make_highchg(data);
|
||||
if (error)
|
||||
goto err_free_irq;
|
||||
|
||||
error = input_register_device(input_dev);
|
||||
if (error)
|
||||
goto err_free_irq;
|
||||
|
@ -317,7 +317,7 @@ err_unmap_regs:
|
||||
err_release_mem:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
err_free_dev:
|
||||
input_free_device(ts_dev->input);
|
||||
input_free_device(input_dev);
|
||||
err_free_mem:
|
||||
kfree(ts_dev);
|
||||
return err;
|
||||
|
@ -396,14 +396,14 @@ static int h3600ts_connect(struct serio *serio, struct serio_driver *drv)
|
||||
set_GPIO_IRQ_edge(GPIO_BITSY_NPOWER_BUTTON, GPIO_RISING_EDGE);
|
||||
|
||||
if (request_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, action_button_handler,
|
||||
IRQF_SHARED | IRQF_DISABLED, "h3600_action", &ts->dev)) {
|
||||
IRQF_SHARED | IRQF_DISABLED, "h3600_action", ts->dev)) {
|
||||
printk(KERN_ERR "h3600ts.c: Could not allocate Action Button IRQ!\n");
|
||||
err = -EBUSY;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
if (request_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, npower_button_handler,
|
||||
IRQF_SHARED | IRQF_DISABLED, "h3600_suspend", &ts->dev)) {
|
||||
IRQF_SHARED | IRQF_DISABLED, "h3600_suspend", ts->dev)) {
|
||||
printk(KERN_ERR "h3600ts.c: Could not allocate Power Button IRQ!\n");
|
||||
err = -EBUSY;
|
||||
goto fail2;
|
||||
@ -439,8 +439,8 @@ static void h3600ts_disconnect(struct serio *serio)
|
||||
{
|
||||
struct h3600_dev *ts = serio_get_drvdata(serio);
|
||||
|
||||
free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, &ts->dev);
|
||||
free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, &ts->dev);
|
||||
free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts->dev);
|
||||
free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts->dev);
|
||||
input_get_device(ts->dev);
|
||||
input_unregister_device(ts->dev);
|
||||
serio_close(serio);
|
||||
|
272
drivers/input/touchscreen/max11801_ts.c
Normal file
272
drivers/input/touchscreen/max11801_ts.c
Normal file
@ -0,0 +1,272 @@
|
||||
/*
|
||||
* Driver for MAXI MAX11801 - A Resistive touch screen controller with
|
||||
* i2c interface
|
||||
*
|
||||
* Copyright (C) 2011 Freescale Semiconductor, Inc.
|
||||
* Author: Zhang Jiejing <jiejing.zhang@freescale.com>
|
||||
*
|
||||
* Based on mcs5000_ts.c
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This driver aims to support the series of MAXI touch chips max11801
|
||||
* through max11803. The main difference between these 4 chips can be
|
||||
* found in the table below:
|
||||
* -----------------------------------------------------
|
||||
* | CHIP | AUTO MODE SUPPORT(FIFO) | INTERFACE |
|
||||
* |----------------------------------------------------|
|
||||
* | max11800 | YES | SPI |
|
||||
* | max11801 | YES | I2C |
|
||||
* | max11802 | NO | SPI |
|
||||
* | max11803 | NO | I2C |
|
||||
* ------------------------------------------------------
|
||||
*
|
||||
* Currently, this driver only supports max11801.
|
||||
*
|
||||
* Data Sheet:
|
||||
* http://www.maxim-ic.com/datasheet/index.mvp/id/5943
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
/* Register Address define */
|
||||
#define GENERNAL_STATUS_REG 0x00
|
||||
#define GENERNAL_CONF_REG 0x01
|
||||
#define MESURE_RES_CONF_REG 0x02
|
||||
#define MESURE_AVER_CONF_REG 0x03
|
||||
#define ADC_SAMPLE_TIME_CONF_REG 0x04
|
||||
#define PANEL_SETUPTIME_CONF_REG 0x05
|
||||
#define DELAY_CONVERSION_CONF_REG 0x06
|
||||
#define TOUCH_DETECT_PULLUP_CONF_REG 0x07
|
||||
#define AUTO_MODE_TIME_CONF_REG 0x08 /* only for max11800/max11801 */
|
||||
#define APERTURE_CONF_REG 0x09 /* only for max11800/max11801 */
|
||||
#define AUX_MESURE_CONF_REG 0x0a
|
||||
#define OP_MODE_CONF_REG 0x0b
|
||||
|
||||
/* FIFO is found only in max11800 and max11801 */
|
||||
#define FIFO_RD_CMD (0x50 << 1)
|
||||
#define MAX11801_FIFO_INT (1 << 2)
|
||||
#define MAX11801_FIFO_OVERFLOW (1 << 3)
|
||||
|
||||
#define XY_BUFSIZE 4
|
||||
#define XY_BUF_OFFSET 4
|
||||
|
||||
#define MAX11801_MAX_X 0xfff
|
||||
#define MAX11801_MAX_Y 0xfff
|
||||
|
||||
#define MEASURE_TAG_OFFSET 2
|
||||
#define MEASURE_TAG_MASK (3 << MEASURE_TAG_OFFSET)
|
||||
#define EVENT_TAG_OFFSET 0
|
||||
#define EVENT_TAG_MASK (3 << EVENT_TAG_OFFSET)
|
||||
#define MEASURE_X_TAG (0 << MEASURE_TAG_OFFSET)
|
||||
#define MEASURE_Y_TAG (1 << MEASURE_TAG_OFFSET)
|
||||
|
||||
/* These are the state of touch event state machine */
|
||||
enum {
|
||||
EVENT_INIT,
|
||||
EVENT_MIDDLE,
|
||||
EVENT_RELEASE,
|
||||
EVENT_FIFO_END
|
||||
};
|
||||
|
||||
struct max11801_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input_dev;
|
||||
};
|
||||
|
||||
static u8 read_register(struct i2c_client *client, int addr)
|
||||
{
|
||||
/* XXX: The chip ignores LSB of register address */
|
||||
return i2c_smbus_read_byte_data(client, addr << 1);
|
||||
}
|
||||
|
||||
static int max11801_write_reg(struct i2c_client *client, int addr, int data)
|
||||
{
|
||||
/* XXX: The chip ignores LSB of register address */
|
||||
return i2c_smbus_write_byte_data(client, addr << 1, data);
|
||||
}
|
||||
|
||||
static irqreturn_t max11801_ts_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct max11801_data *data = dev_id;
|
||||
struct i2c_client *client = data->client;
|
||||
int status, i, ret;
|
||||
u8 buf[XY_BUFSIZE];
|
||||
int x = -1;
|
||||
int y = -1;
|
||||
|
||||
status = read_register(data->client, GENERNAL_STATUS_REG);
|
||||
|
||||
if (status & (MAX11801_FIFO_INT | MAX11801_FIFO_OVERFLOW)) {
|
||||
status = read_register(data->client, GENERNAL_STATUS_REG);
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_CMD,
|
||||
XY_BUFSIZE, buf);
|
||||
|
||||
/*
|
||||
* We should get 4 bytes buffer that contains X,Y
|
||||
* and event tag
|
||||
*/
|
||||
if (ret < XY_BUFSIZE)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < XY_BUFSIZE; i += XY_BUFSIZE / 2) {
|
||||
if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_X_TAG)
|
||||
x = (buf[i] << XY_BUF_OFFSET) +
|
||||
(buf[i + 1] >> XY_BUF_OFFSET);
|
||||
else if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_Y_TAG)
|
||||
y = (buf[i] << XY_BUF_OFFSET) +
|
||||
(buf[i + 1] >> XY_BUF_OFFSET);
|
||||
}
|
||||
|
||||
if ((buf[1] & EVENT_TAG_MASK) != (buf[3] & EVENT_TAG_MASK))
|
||||
goto out;
|
||||
|
||||
switch (buf[1] & EVENT_TAG_MASK) {
|
||||
case EVENT_INIT:
|
||||
/* fall through */
|
||||
case EVENT_MIDDLE:
|
||||
input_report_abs(data->input_dev, ABS_X, x);
|
||||
input_report_abs(data->input_dev, ABS_Y, y);
|
||||
input_event(data->input_dev, EV_KEY, BTN_TOUCH, 1);
|
||||
input_sync(data->input_dev);
|
||||
break;
|
||||
|
||||
case EVENT_RELEASE:
|
||||
input_event(data->input_dev, EV_KEY, BTN_TOUCH, 0);
|
||||
input_sync(data->input_dev);
|
||||
break;
|
||||
|
||||
case EVENT_FIFO_END:
|
||||
break;
|
||||
}
|
||||
}
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void __devinit max11801_ts_phy_init(struct max11801_data *data)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
/* Average X,Y, take 16 samples, average eight media sample */
|
||||
max11801_write_reg(client, MESURE_AVER_CONF_REG, 0xff);
|
||||
/* X,Y panel setup time set to 20us */
|
||||
max11801_write_reg(client, PANEL_SETUPTIME_CONF_REG, 0x11);
|
||||
/* Rough pullup time (2uS), Fine pullup time (10us) */
|
||||
max11801_write_reg(client, TOUCH_DETECT_PULLUP_CONF_REG, 0x10);
|
||||
/* Auto mode init period = 5ms , scan period = 5ms*/
|
||||
max11801_write_reg(client, AUTO_MODE_TIME_CONF_REG, 0xaa);
|
||||
/* Aperture X,Y set to +- 4LSB */
|
||||
max11801_write_reg(client, APERTURE_CONF_REG, 0x33);
|
||||
/* Enable Power, enable Automode, enable Aperture, enable Average X,Y */
|
||||
max11801_write_reg(client, OP_MODE_CONF_REG, 0x36);
|
||||
}
|
||||
|
||||
static int __devinit max11801_ts_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct max11801_data *data;
|
||||
struct input_dev *input_dev;
|
||||
int error;
|
||||
|
||||
data = kzalloc(sizeof(struct max11801_data), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!data || !input_dev) {
|
||||
dev_err(&client->dev, "Failed to allocate memory\n");
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
data->client = client;
|
||||
data->input_dev = input_dev;
|
||||
|
||||
input_dev->name = "max11801_ts";
|
||||
input_dev->id.bustype = BUS_I2C;
|
||||
input_dev->dev.parent = &client->dev;
|
||||
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
input_set_abs_params(input_dev, ABS_X, 0, MAX11801_MAX_X, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, MAX11801_MAX_Y, 0, 0);
|
||||
input_set_drvdata(input_dev, data);
|
||||
|
||||
max11801_ts_phy_init(data);
|
||||
|
||||
error = request_threaded_irq(client->irq, NULL, max11801_ts_interrupt,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
"max11801_ts", data);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Failed to register interrupt\n");
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
error = input_register_device(data->input_dev);
|
||||
if (error)
|
||||
goto err_free_irq;
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(client->irq, data);
|
||||
err_free_mem:
|
||||
input_free_device(input_dev);
|
||||
kfree(data);
|
||||
return error;
|
||||
}
|
||||
|
||||
static __devexit int max11801_ts_remove(struct i2c_client *client)
|
||||
{
|
||||
struct max11801_data *data = i2c_get_clientdata(client);
|
||||
|
||||
free_irq(client->irq, data);
|
||||
input_unregister_device(data->input_dev);
|
||||
kfree(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max11801_ts_id[] = {
|
||||
{"max11801", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max11801_ts_id);
|
||||
|
||||
static struct i2c_driver max11801_ts_driver = {
|
||||
.driver = {
|
||||
.name = "max11801_ts",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = max11801_ts_id,
|
||||
.probe = max11801_ts_probe,
|
||||
.remove = __devexit_p(max11801_ts_remove),
|
||||
};
|
||||
|
||||
static int __init max11801_ts_init(void)
|
||||
{
|
||||
return i2c_add_driver(&max11801_ts_driver);
|
||||
}
|
||||
|
||||
static void __exit max11801_ts_exit(void)
|
||||
{
|
||||
i2c_del_driver(&max11801_ts_driver);
|
||||
}
|
||||
|
||||
module_init(max11801_ts_init);
|
||||
module_exit(max11801_ts_exit);
|
||||
|
||||
MODULE_AUTHOR("Zhang Jiejing <jiejing.zhang@freescale.com>");
|
||||
MODULE_DESCRIPTION("Touchscreen driver for MAXI MAX11801 controller");
|
||||
MODULE_LICENSE("GPL");
|
@ -27,9 +27,6 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/tsc2007.h>
|
||||
|
||||
#define TS_POLL_DELAY 1 /* ms delay between samples */
|
||||
#define TS_POLL_PERIOD 1 /* ms delay between samples */
|
||||
|
||||
#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
|
||||
#define TSC2007_MEASURE_AUX (0x2 << 4)
|
||||
#define TSC2007_MEASURE_TEMP1 (0x4 << 4)
|
||||
@ -75,6 +72,9 @@ struct tsc2007 {
|
||||
|
||||
u16 model;
|
||||
u16 x_plate_ohms;
|
||||
u16 max_rt;
|
||||
unsigned long poll_delay;
|
||||
unsigned long poll_period;
|
||||
|
||||
bool pendown;
|
||||
int irq;
|
||||
@ -156,6 +156,7 @@ static void tsc2007_work(struct work_struct *work)
|
||||
{
|
||||
struct tsc2007 *ts =
|
||||
container_of(to_delayed_work(work), struct tsc2007, work);
|
||||
bool debounced = false;
|
||||
struct ts_event tc;
|
||||
u32 rt;
|
||||
|
||||
@ -184,13 +185,14 @@ static void tsc2007_work(struct work_struct *work)
|
||||
tsc2007_read_values(ts, &tc);
|
||||
|
||||
rt = tsc2007_calculate_pressure(ts, &tc);
|
||||
if (rt > MAX_12BIT) {
|
||||
if (rt > ts->max_rt) {
|
||||
/*
|
||||
* Sample found inconsistent by debouncing or pressure is
|
||||
* beyond the maximum. Don't report it to user space,
|
||||
* repeat at least once more the measurement.
|
||||
*/
|
||||
dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
|
||||
debounced = true;
|
||||
goto out;
|
||||
|
||||
}
|
||||
@ -225,9 +227,9 @@ static void tsc2007_work(struct work_struct *work)
|
||||
}
|
||||
|
||||
out:
|
||||
if (ts->pendown)
|
||||
if (ts->pendown || debounced)
|
||||
schedule_delayed_work(&ts->work,
|
||||
msecs_to_jiffies(TS_POLL_PERIOD));
|
||||
msecs_to_jiffies(ts->poll_period));
|
||||
else
|
||||
enable_irq(ts->irq);
|
||||
}
|
||||
@ -239,7 +241,7 @@ static irqreturn_t tsc2007_irq(int irq, void *handle)
|
||||
if (!ts->get_pendown_state || likely(ts->get_pendown_state())) {
|
||||
disable_irq_nosync(ts->irq);
|
||||
schedule_delayed_work(&ts->work,
|
||||
msecs_to_jiffies(TS_POLL_DELAY));
|
||||
msecs_to_jiffies(ts->poll_delay));
|
||||
}
|
||||
|
||||
if (ts->clear_penirq)
|
||||
@ -292,6 +294,9 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
|
||||
|
||||
ts->model = pdata->model;
|
||||
ts->x_plate_ohms = pdata->x_plate_ohms;
|
||||
ts->max_rt = pdata->max_rt ? : MAX_12BIT;
|
||||
ts->poll_delay = pdata->poll_delay ? : 1;
|
||||
ts->poll_period = pdata->poll_period ? : 1;
|
||||
ts->get_pendown_state = pdata->get_pendown_state;
|
||||
ts->clear_penirq = pdata->clear_penirq;
|
||||
|
||||
@ -305,9 +310,10 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, pdata->fuzzx, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, pdata->fuzzy, 0);
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT,
|
||||
pdata->fuzzz, 0);
|
||||
|
||||
if (pdata->init_platform_hw)
|
||||
pdata->init_platform_hw();
|
||||
|
@ -3,14 +3,15 @@
|
||||
|
||||
struct gpio_keys_button {
|
||||
/* Configuration parameters */
|
||||
int code; /* input event code (KEY_*, SW_*) */
|
||||
unsigned int code; /* input event code (KEY_*, SW_*) */
|
||||
int gpio;
|
||||
int active_low;
|
||||
char *desc;
|
||||
int type; /* input event type (EV_KEY, EV_SW) */
|
||||
const char *desc;
|
||||
unsigned int type; /* input event type (EV_KEY, EV_SW, EV_ABS) */
|
||||
int wakeup; /* configure the button as a wake-up source */
|
||||
int debounce_interval; /* debounce ticks interval in msecs */
|
||||
bool can_disable;
|
||||
int value; /* axis value for EV_ABS */
|
||||
};
|
||||
|
||||
struct gpio_keys_platform_data {
|
||||
@ -21,6 +22,7 @@ struct gpio_keys_platform_data {
|
||||
unsigned int rep:1; /* enable input subsystem auto repeat */
|
||||
int (*enable)(struct device *dev);
|
||||
void (*disable)(struct device *dev);
|
||||
const char *name; /* input device name */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
20
include/linux/i2c/mpr121_touchkey.h
Normal file
20
include/linux/i2c/mpr121_touchkey.h
Normal file
@ -0,0 +1,20 @@
|
||||
/* Header file for Freescale MPR121 Capacitive Touch Sensor */
|
||||
|
||||
#ifndef _MPR121_TOUCHKEY_H
|
||||
#define _MPR121_TOUCHKEY_H
|
||||
|
||||
/**
|
||||
* struct mpr121_platform_data - platform data for mpr121 sensor
|
||||
* @keymap: pointer to array of KEY_* values representing keymap
|
||||
* @keymap_size: size of the keymap
|
||||
* @wakeup: configure the button as a wake-up source
|
||||
* @vdd_uv: VDD voltage in uV
|
||||
*/
|
||||
struct mpr121_platform_data {
|
||||
const unsigned short *keymap;
|
||||
unsigned int keymap_size;
|
||||
bool wakeup;
|
||||
int vdd_uv;
|
||||
};
|
||||
|
||||
#endif /* _MPR121_TOUCHKEY_H */
|
@ -6,6 +6,13 @@
|
||||
struct tsc2007_platform_data {
|
||||
u16 model; /* 2007. */
|
||||
u16 x_plate_ohms;
|
||||
u16 max_rt; /* max. resistance above which samples are ignored */
|
||||
unsigned long poll_delay; /* delay (in ms) after pen-down event
|
||||
before polling starts */
|
||||
unsigned long poll_period; /* time (in ms) between samples */
|
||||
int fuzzx; /* fuzz factor for X, Y and pressure axes */
|
||||
int fuzzy;
|
||||
int fuzzz;
|
||||
|
||||
int (*get_pendown_state)(void);
|
||||
void (*clear_penirq)(void); /* If needed, clear 2nd level
|
||||
|
@ -6,7 +6,7 @@
|
||||
* The platform_data for the device's "struct device" holds this
|
||||
* information.
|
||||
*
|
||||
* Copyright 2009 Analog Devices Inc.
|
||||
* Copyright 2009-2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
@ -58,6 +58,7 @@ struct ad714x_platform_data {
|
||||
struct ad714x_button_plat *button;
|
||||
unsigned short stage_cfg_reg[STAGE_NUM][STAGE_CFGREG_NUM];
|
||||
unsigned short sys_cfg_reg[SYS_CFGREG_NUM];
|
||||
unsigned long irqflags;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
213
include/linux/input/adp5589.h
Normal file
213
include/linux/input/adp5589.h
Normal file
@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Analog Devices ADP5589 I/O Expander and QWERTY Keypad Controller
|
||||
*
|
||||
* Copyright 2010-2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#ifndef _ADP5589_H
|
||||
#define _ADP5589_H
|
||||
|
||||
#define ADP5589_ID 0x00
|
||||
#define ADP5589_INT_STATUS 0x01
|
||||
#define ADP5589_STATUS 0x02
|
||||
#define ADP5589_FIFO_1 0x03
|
||||
#define ADP5589_FIFO_2 0x04
|
||||
#define ADP5589_FIFO_3 0x05
|
||||
#define ADP5589_FIFO_4 0x06
|
||||
#define ADP5589_FIFO_5 0x07
|
||||
#define ADP5589_FIFO_6 0x08
|
||||
#define ADP5589_FIFO_7 0x09
|
||||
#define ADP5589_FIFO_8 0x0A
|
||||
#define ADP5589_FIFO_9 0x0B
|
||||
#define ADP5589_FIFO_10 0x0C
|
||||
#define ADP5589_FIFO_11 0x0D
|
||||
#define ADP5589_FIFO_12 0x0E
|
||||
#define ADP5589_FIFO_13 0x0F
|
||||
#define ADP5589_FIFO_14 0x10
|
||||
#define ADP5589_FIFO_15 0x11
|
||||
#define ADP5589_FIFO_16 0x12
|
||||
#define ADP5589_GPI_INT_STAT_A 0x13
|
||||
#define ADP5589_GPI_INT_STAT_B 0x14
|
||||
#define ADP5589_GPI_INT_STAT_C 0x15
|
||||
#define ADP5589_GPI_STATUS_A 0x16
|
||||
#define ADP5589_GPI_STATUS_B 0x17
|
||||
#define ADP5589_GPI_STATUS_C 0x18
|
||||
#define ADP5589_RPULL_CONFIG_A 0x19
|
||||
#define ADP5589_RPULL_CONFIG_B 0x1A
|
||||
#define ADP5589_RPULL_CONFIG_C 0x1B
|
||||
#define ADP5589_RPULL_CONFIG_D 0x1C
|
||||
#define ADP5589_RPULL_CONFIG_E 0x1D
|
||||
#define ADP5589_GPI_INT_LEVEL_A 0x1E
|
||||
#define ADP5589_GPI_INT_LEVEL_B 0x1F
|
||||
#define ADP5589_GPI_INT_LEVEL_C 0x20
|
||||
#define ADP5589_GPI_EVENT_EN_A 0x21
|
||||
#define ADP5589_GPI_EVENT_EN_B 0x22
|
||||
#define ADP5589_GPI_EVENT_EN_C 0x23
|
||||
#define ADP5589_GPI_INTERRUPT_EN_A 0x24
|
||||
#define ADP5589_GPI_INTERRUPT_EN_B 0x25
|
||||
#define ADP5589_GPI_INTERRUPT_EN_C 0x26
|
||||
#define ADP5589_DEBOUNCE_DIS_A 0x27
|
||||
#define ADP5589_DEBOUNCE_DIS_B 0x28
|
||||
#define ADP5589_DEBOUNCE_DIS_C 0x29
|
||||
#define ADP5589_GPO_DATA_OUT_A 0x2A
|
||||
#define ADP5589_GPO_DATA_OUT_B 0x2B
|
||||
#define ADP5589_GPO_DATA_OUT_C 0x2C
|
||||
#define ADP5589_GPO_OUT_MODE_A 0x2D
|
||||
#define ADP5589_GPO_OUT_MODE_B 0x2E
|
||||
#define ADP5589_GPO_OUT_MODE_C 0x2F
|
||||
#define ADP5589_GPIO_DIRECTION_A 0x30
|
||||
#define ADP5589_GPIO_DIRECTION_B 0x31
|
||||
#define ADP5589_GPIO_DIRECTION_C 0x32
|
||||
#define ADP5589_UNLOCK1 0x33
|
||||
#define ADP5589_UNLOCK2 0x34
|
||||
#define ADP5589_EXT_LOCK_EVENT 0x35
|
||||
#define ADP5589_UNLOCK_TIMERS 0x36
|
||||
#define ADP5589_LOCK_CFG 0x37
|
||||
#define ADP5589_RESET1_EVENT_A 0x38
|
||||
#define ADP5589_RESET1_EVENT_B 0x39
|
||||
#define ADP5589_RESET1_EVENT_C 0x3A
|
||||
#define ADP5589_RESET2_EVENT_A 0x3B
|
||||
#define ADP5589_RESET2_EVENT_B 0x3C
|
||||
#define ADP5589_RESET_CFG 0x3D
|
||||
#define ADP5589_PWM_OFFT_LOW 0x3E
|
||||
#define ADP5589_PWM_OFFT_HIGH 0x3F
|
||||
#define ADP5589_PWM_ONT_LOW 0x40
|
||||
#define ADP5589_PWM_ONT_HIGH 0x41
|
||||
#define ADP5589_PWM_CFG 0x42
|
||||
#define ADP5589_CLOCK_DIV_CFG 0x43
|
||||
#define ADP5589_LOGIC_1_CFG 0x44
|
||||
#define ADP5589_LOGIC_2_CFG 0x45
|
||||
#define ADP5589_LOGIC_FF_CFG 0x46
|
||||
#define ADP5589_LOGIC_INT_EVENT_EN 0x47
|
||||
#define ADP5589_POLL_PTIME_CFG 0x48
|
||||
#define ADP5589_PIN_CONFIG_A 0x49
|
||||
#define ADP5589_PIN_CONFIG_B 0x4A
|
||||
#define ADP5589_PIN_CONFIG_C 0x4B
|
||||
#define ADP5589_PIN_CONFIG_D 0x4C
|
||||
#define ADP5589_GENERAL_CFG 0x4D
|
||||
#define ADP5589_INT_EN 0x4E
|
||||
|
||||
#define ADP5589_DEVICE_ID_MASK 0xF
|
||||
|
||||
/* Put one of these structures in i2c_board_info platform_data */
|
||||
|
||||
#define ADP5589_KEYMAPSIZE 88
|
||||
|
||||
#define ADP5589_GPI_PIN_ROW0 97
|
||||
#define ADP5589_GPI_PIN_ROW1 98
|
||||
#define ADP5589_GPI_PIN_ROW2 99
|
||||
#define ADP5589_GPI_PIN_ROW3 100
|
||||
#define ADP5589_GPI_PIN_ROW4 101
|
||||
#define ADP5589_GPI_PIN_ROW5 102
|
||||
#define ADP5589_GPI_PIN_ROW6 103
|
||||
#define ADP5589_GPI_PIN_ROW7 104
|
||||
#define ADP5589_GPI_PIN_COL0 105
|
||||
#define ADP5589_GPI_PIN_COL1 106
|
||||
#define ADP5589_GPI_PIN_COL2 107
|
||||
#define ADP5589_GPI_PIN_COL3 108
|
||||
#define ADP5589_GPI_PIN_COL4 109
|
||||
#define ADP5589_GPI_PIN_COL5 110
|
||||
#define ADP5589_GPI_PIN_COL6 111
|
||||
#define ADP5589_GPI_PIN_COL7 112
|
||||
#define ADP5589_GPI_PIN_COL8 113
|
||||
#define ADP5589_GPI_PIN_COL9 114
|
||||
#define ADP5589_GPI_PIN_COL10 115
|
||||
#define GPI_LOGIC1 116
|
||||
#define GPI_LOGIC2 117
|
||||
|
||||
#define ADP5589_GPI_PIN_ROW_BASE ADP5589_GPI_PIN_ROW0
|
||||
#define ADP5589_GPI_PIN_ROW_END ADP5589_GPI_PIN_ROW7
|
||||
#define ADP5589_GPI_PIN_COL_BASE ADP5589_GPI_PIN_COL0
|
||||
#define ADP5589_GPI_PIN_COL_END ADP5589_GPI_PIN_COL10
|
||||
|
||||
#define ADP5589_GPI_PIN_BASE ADP5589_GPI_PIN_ROW_BASE
|
||||
#define ADP5589_GPI_PIN_END ADP5589_GPI_PIN_COL_END
|
||||
|
||||
#define ADP5589_GPIMAPSIZE_MAX (ADP5589_GPI_PIN_END - ADP5589_GPI_PIN_BASE + 1)
|
||||
|
||||
struct adp5589_gpi_map {
|
||||
unsigned short pin;
|
||||
unsigned short sw_evt;
|
||||
};
|
||||
|
||||
/* scan_cycle_time */
|
||||
#define ADP5589_SCAN_CYCLE_10ms 0
|
||||
#define ADP5589_SCAN_CYCLE_20ms 1
|
||||
#define ADP5589_SCAN_CYCLE_30ms 2
|
||||
#define ADP5589_SCAN_CYCLE_40ms 3
|
||||
|
||||
/* RESET_CFG */
|
||||
#define RESET_PULSE_WIDTH_500us 0
|
||||
#define RESET_PULSE_WIDTH_1ms 1
|
||||
#define RESET_PULSE_WIDTH_2ms 2
|
||||
#define RESET_PULSE_WIDTH_10ms 3
|
||||
|
||||
#define RESET_TRIG_TIME_0ms (0 << 2)
|
||||
#define RESET_TRIG_TIME_1000ms (1 << 2)
|
||||
#define RESET_TRIG_TIME_1500ms (2 << 2)
|
||||
#define RESET_TRIG_TIME_2000ms (3 << 2)
|
||||
#define RESET_TRIG_TIME_2500ms (4 << 2)
|
||||
#define RESET_TRIG_TIME_3000ms (5 << 2)
|
||||
#define RESET_TRIG_TIME_3500ms (6 << 2)
|
||||
#define RESET_TRIG_TIME_4000ms (7 << 2)
|
||||
|
||||
#define RESET_PASSTHRU_EN (1 << 5)
|
||||
#define RESET1_POL_HIGH (1 << 6)
|
||||
#define RESET1_POL_LOW (0 << 6)
|
||||
#define RESET2_POL_HIGH (1 << 7)
|
||||
#define RESET2_POL_LOW (0 << 7)
|
||||
|
||||
/* Mask Bits:
|
||||
* C C C C C C C C C C C | R R R R R R R R
|
||||
* 1 9 8 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0
|
||||
* 0
|
||||
* ---------------- BIT ------------------
|
||||
* 1 1 1 1 1 1 1 1 1 0 0 | 0 0 0 0 0 0 0 0
|
||||
* 8 7 6 5 4 3 2 1 0 9 8 | 7 6 5 4 3 2 1 0
|
||||
*/
|
||||
|
||||
#define ADP_ROW(x) (1 << (x))
|
||||
#define ADP_COL(x) (1 << (x + 8))
|
||||
|
||||
struct adp5589_kpad_platform_data {
|
||||
unsigned keypad_en_mask; /* Keypad (Rows/Columns) enable mask */
|
||||
const unsigned short *keymap; /* Pointer to keymap */
|
||||
unsigned short keymapsize; /* Keymap size */
|
||||
bool repeat; /* Enable key repeat */
|
||||
bool en_keylock; /* Enable key lock feature */
|
||||
unsigned char unlock_key1; /* Unlock Key 1 */
|
||||
unsigned char unlock_key2; /* Unlock Key 2 */
|
||||
unsigned char unlock_timer; /* Time in seconds [0..7] between the two unlock keys 0=disable */
|
||||
unsigned char scan_cycle_time; /* Time between consecutive scan cycles */
|
||||
unsigned char reset_cfg; /* Reset config */
|
||||
unsigned short reset1_key_1; /* Reset Key 1 */
|
||||
unsigned short reset1_key_2; /* Reset Key 2 */
|
||||
unsigned short reset1_key_3; /* Reset Key 3 */
|
||||
unsigned short reset2_key_1; /* Reset Key 1 */
|
||||
unsigned short reset2_key_2; /* Reset Key 2 */
|
||||
unsigned debounce_dis_mask; /* Disable debounce mask */
|
||||
unsigned pull_dis_mask; /* Disable all pull resistors mask */
|
||||
unsigned pullup_en_100k; /* Pull-Up 100k Enable Mask */
|
||||
unsigned pullup_en_300k; /* Pull-Up 300k Enable Mask */
|
||||
unsigned pulldown_en_300k; /* Pull-Down 300k Enable Mask */
|
||||
const struct adp5589_gpi_map *gpimap;
|
||||
unsigned short gpimapsize;
|
||||
const struct adp5589_gpio_platform_data *gpio_data;
|
||||
};
|
||||
|
||||
struct i2c_client; /* forward declaration */
|
||||
|
||||
struct adp5589_gpio_platform_data {
|
||||
int gpio_start; /* GPIO Chip base # */
|
||||
int (*setup)(struct i2c_client *client,
|
||||
int gpio, unsigned ngpio,
|
||||
void *context);
|
||||
int (*teardown)(struct i2c_client *client,
|
||||
int gpio, unsigned ngpio,
|
||||
void *context);
|
||||
void *context;
|
||||
};
|
||||
|
||||
#endif
|
@ -10,6 +10,7 @@ struct rotary_encoder_platform_data {
|
||||
unsigned int inverted_b;
|
||||
bool relative_axis;
|
||||
bool rollover;
|
||||
bool half_period;
|
||||
};
|
||||
|
||||
#endif /* __ROTARY_ENCODER_H__ */
|
||||
|
@ -14,7 +14,8 @@ enum ads7846_filter {
|
||||
struct ads7846_platform_data {
|
||||
u16 model; /* 7843, 7845, 7846, 7873. */
|
||||
u16 vref_delay_usecs; /* 0 for external vref; etc */
|
||||
u16 vref_mv; /* external vref value, milliVolts */
|
||||
u16 vref_mv; /* external vref value, milliVolts
|
||||
* ads7846: if 0, use internal vref */
|
||||
bool keep_vref_on; /* set to keep vref on for differential
|
||||
* measurements as well */
|
||||
bool swap_xy; /* swap x and y axes */
|
||||
|
Loading…
Reference in New Issue
Block a user