Merge branches 'for-4.0/upstream-fixes', 'for-4.1/genius', 'for-4.1/huion-uclogic-merge', 'for-4.1/i2c-hid', 'for-4.1/kconfig-drop-expert-dependency', 'for-4.1/logitech', 'for-4.1/multitouch', 'for-4.1/rmi', 'for-4.1/sony', 'for-4.1/upstream' and 'for-4.1/wacom' into for-linus

This commit is contained in:
7668 changed files with 355131 additions and 185271 deletions

View file

@ -62,26 +62,6 @@ struct evdev_client {
struct input_event buffer[];
};
static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid)
{
switch (clkid) {
case CLOCK_REALTIME:
client->clk_type = EV_CLK_REAL;
break;
case CLOCK_MONOTONIC:
client->clk_type = EV_CLK_MONO;
break;
case CLOCK_BOOTTIME:
client->clk_type = EV_CLK_BOOT;
break;
default:
return -EINVAL;
}
return 0;
}
/* flush queued events of type @type, caller must hold client->buffer_lock */
static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
{
@ -128,10 +108,8 @@ static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
client->head = head;
}
/* queue SYN_DROPPED event */
static void evdev_queue_syn_dropped(struct evdev_client *client)
static void __evdev_queue_syn_dropped(struct evdev_client *client)
{
unsigned long flags;
struct input_event ev;
ktime_t time;
@ -146,8 +124,6 @@ static void evdev_queue_syn_dropped(struct evdev_client *client)
ev.code = SYN_DROPPED;
ev.value = 0;
spin_lock_irqsave(&client->buffer_lock, flags);
client->buffer[client->head++] = ev;
client->head &= client->bufsize - 1;
@ -156,8 +132,53 @@ static void evdev_queue_syn_dropped(struct evdev_client *client)
client->tail = (client->head - 1) & (client->bufsize - 1);
client->packet_head = client->tail;
}
}
static void evdev_queue_syn_dropped(struct evdev_client *client)
{
unsigned long flags;
spin_lock_irqsave(&client->buffer_lock, flags);
__evdev_queue_syn_dropped(client);
spin_unlock_irqrestore(&client->buffer_lock, flags);
}
static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid)
{
unsigned long flags;
if (client->clk_type == clkid)
return 0;
switch (clkid) {
case CLOCK_REALTIME:
client->clk_type = EV_CLK_REAL;
break;
case CLOCK_MONOTONIC:
client->clk_type = EV_CLK_MONO;
break;
case CLOCK_BOOTTIME:
client->clk_type = EV_CLK_BOOT;
break;
default:
return -EINVAL;
}
/*
* Flush pending events and queue SYN_DROPPED event,
* but only if the queue is not empty.
*/
spin_lock_irqsave(&client->buffer_lock, flags);
if (client->head != client->tail) {
client->packet_head = client->head = client->tail;
__evdev_queue_syn_dropped(client);
}
spin_unlock_irqrestore(&client->buffer_lock, flags);
return 0;
}
static void __pass_event(struct evdev_client *client,

View file

@ -88,10 +88,13 @@ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots,
goto err_mem;
}
/* Mark slots as 'unused' */
/* Mark slots as 'inactive' */
for (i = 0; i < num_slots; i++)
input_mt_set_value(&mt->slots[i], ABS_MT_TRACKING_ID, -1);
/* Mark slots as 'unused' */
mt->frame = 1;
dev->mt = mt;
return 0;
err_mem:
@ -293,7 +296,7 @@ void input_mt_sync_frame(struct input_dev *dev)
}
EXPORT_SYMBOL(input_mt_sync_frame);
static int adjust_dual(int *begin, int step, int *end, int eq)
static int adjust_dual(int *begin, int step, int *end, int eq, int mu)
{
int f, *p, s, c;
@ -311,9 +314,10 @@ static int adjust_dual(int *begin, int step, int *end, int eq)
s = *p;
c = (f + s + 1) / 2;
if (c == 0 || (c > 0 && !eq))
if (c == 0 || (c > mu && (!eq || mu > 0)))
return 0;
if (s < 0)
/* Improve convergence for positive matrices by penalizing overcovers */
if (s < 0 && mu <= 0)
c *= 2;
for (p = begin; p != end; p += step)
@ -322,23 +326,24 @@ static int adjust_dual(int *begin, int step, int *end, int eq)
return (c < s && s <= 0) || (f >= 0 && f < c);
}
static void find_reduced_matrix(int *w, int nr, int nc, int nrc)
static void find_reduced_matrix(int *w, int nr, int nc, int nrc, int mu)
{
int i, k, sum;
for (k = 0; k < nrc; k++) {
for (i = 0; i < nr; i++)
adjust_dual(w + i, nr, w + i + nrc, nr <= nc);
adjust_dual(w + i, nr, w + i + nrc, nr <= nc, mu);
sum = 0;
for (i = 0; i < nrc; i += nr)
sum += adjust_dual(w + i, 1, w + i + nr, nc <= nr);
sum += adjust_dual(w + i, 1, w + i + nr, nc <= nr, mu);
if (!sum)
break;
}
}
static int input_mt_set_matrix(struct input_mt *mt,
const struct input_mt_pos *pos, int num_pos)
const struct input_mt_pos *pos, int num_pos,
int mu)
{
const struct input_mt_pos *p;
struct input_mt_slot *s;
@ -352,7 +357,7 @@ static int input_mt_set_matrix(struct input_mt *mt,
y = input_mt_get_value(s, ABS_MT_POSITION_Y);
for (p = pos; p != pos + num_pos; p++) {
int dx = x - p->x, dy = y - p->y;
*w++ = dx * dx + dy * dy;
*w++ = dx * dx + dy * dy - mu;
}
}
@ -393,17 +398,24 @@ static void input_mt_set_slots(struct input_mt *mt,
* @slots: the slot assignment to be filled
* @pos: the position array to match
* @num_pos: number of positions
* @dmax: maximum ABS_MT_POSITION displacement (zero for infinite)
*
* Performs a best match against the current contacts and returns
* the slot assignment list. New contacts are assigned to unused
* slots.
*
* The assignments are balanced so that all coordinate displacements are
* below the euclidian distance dmax. If no such assignment can be found,
* some contacts are assigned to unused slots.
*
* Returns zero on success, or negative error in case of failure.
*/
int input_mt_assign_slots(struct input_dev *dev, int *slots,
const struct input_mt_pos *pos, int num_pos)
const struct input_mt_pos *pos, int num_pos,
int dmax)
{
struct input_mt *mt = dev->mt;
int mu = 2 * dmax * dmax;
int nrc;
if (!mt || !mt->red)
@ -413,8 +425,8 @@ int input_mt_assign_slots(struct input_dev *dev, int *slots,
if (num_pos < 1)
return 0;
nrc = input_mt_set_matrix(mt, pos, num_pos);
find_reduced_matrix(mt->red, num_pos, nrc / num_pos, nrc);
nrc = input_mt_set_matrix(mt, pos, num_pos, mu);
find_reduced_matrix(mt->red, num_pos, nrc / num_pos, nrc, mu);
input_mt_set_slots(mt, slots, num_pos);
return 0;
@ -430,6 +442,8 @@ EXPORT_SYMBOL(input_mt_assign_slots);
* set the key on the first unused slot and return.
*
* If no available slot can be found, -1 is returned.
* Note that for this function to work properly, input_mt_sync_frame() has
* to be called at each frame.
*/
int input_mt_get_slot_by_key(struct input_dev *dev, int key)
{
@ -444,7 +458,7 @@ int input_mt_get_slot_by_key(struct input_dev *dev, int key)
return s - mt->slots;
for (s = mt->slots; s != mt->slots + mt->num_slots; s++)
if (!input_mt_is_active(s)) {
if (!input_mt_is_active(s) && !input_mt_is_used(mt, s)) {
s->key = key;
return s - mt->slots;
}

View file

@ -100,23 +100,24 @@ static unsigned int input_to_handler(struct input_handle *handle,
struct input_value *end = vals;
struct input_value *v;
for (v = vals; v != vals + count; v++) {
if (handler->filter &&
handler->filter(handle, v->type, v->code, v->value))
continue;
if (end != v)
*end = *v;
end++;
if (handler->filter) {
for (v = vals; v != vals + count; v++) {
if (handler->filter(handle, v->type, v->code, v->value))
continue;
if (end != v)
*end = *v;
end++;
}
count = end - vals;
}
count = end - vals;
if (!count)
return 0;
if (handler->events)
handler->events(handle, vals, count);
else if (handler->event)
for (v = vals; v != end; v++)
for (v = vals; v != vals + count; v++)
handler->event(handle, v->type, v->code, v->value);
return count;
@ -143,8 +144,11 @@ static void input_pass_values(struct input_dev *dev,
count = input_to_handler(handle, vals, count);
} else {
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open)
if (handle->open) {
count = input_to_handler(handle, vals, count);
if (!count)
break;
}
}
rcu_read_unlock();
@ -152,12 +156,14 @@ static void input_pass_values(struct input_dev *dev,
add_input_randomness(vals->type, vals->code, vals->value);
/* trigger auto repeat for key events */
for (v = vals; v != vals + count; v++) {
if (v->type == EV_KEY && v->value != 2) {
if (v->value)
input_start_autorepeat(dev, v->code);
else
input_stop_autorepeat(dev);
if (test_bit(EV_REP, dev->evbit) && test_bit(EV_KEY, dev->evbit)) {
for (v = vals; v != vals + count; v++) {
if (v->type == EV_KEY && v->value != 2) {
if (v->value)
input_start_autorepeat(dev, v->code);
else
input_stop_autorepeat(dev);
}
}
}
}

View file

@ -535,8 +535,7 @@ static int adi_connect(struct gameport *gameport, struct gameport_driver *drv)
}
}
fail2: for (i = 0; i < 2; i++)
if (port->adi[i].dev)
input_free_device(port->adi[i].dev);
input_free_device(port->adi[i].dev);
gameport_close(gameport);
fail1: gameport_set_drvdata(gameport, NULL);
kfree(port);

View file

@ -568,6 +568,16 @@ config KEYBOARD_STMPE
To compile this driver as a module, choose M here: the module will be
called stmpe-keypad.
config KEYBOARD_SUN4I_LRADC
tristate "Allwinner sun4i low res adc attached tablet keys support"
depends on ARCH_SUNXI
help
This selects support for the Allwinner low res adc attached tablet
keys found on Allwinner sunxi SoCs.
To compile this driver as a module, choose M here: the
module will be called sun4i-lradc-keys.
config KEYBOARD_DAVINCI
tristate "TI DaVinci Key Scan"
depends on ARCH_DAVINCI_DM365

View file

@ -53,6 +53,7 @@ obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o
obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
obj-$(CONFIG_KEYBOARD_ST_KEYSCAN) += st-keyscan.o
obj-$(CONFIG_KEYBOARD_SUN4I_LRADC) += sun4i-lradc-keys.o
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o
obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o

View file

@ -170,7 +170,7 @@ static unsigned char atakbd_keycode[0x72] = { /* American layout */
[93] = KEY_KPASTERISK,
[94] = KEY_KPPLUS,
[95] = KEY_HELP,
[96] = KEY_BACKSLASH, /* FIXME: '<' */
[96] = KEY_102ND,
[97] = KEY_KPASTERISK, /* FIXME */
[98] = KEY_KPSLASH,
[99] = KEY_KPLEFTPAREN,

View file

@ -1399,8 +1399,8 @@ static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t coun
static ssize_t atkbd_show_force_release(struct atkbd *atkbd, char *buf)
{
size_t len = bitmap_scnlistprintf(buf, PAGE_SIZE - 2,
atkbd->force_release_mask, ATKBD_KEYMAP_SIZE);
size_t len = scnprintf(buf, PAGE_SIZE - 1, "%*pbl",
ATKBD_KEYMAP_SIZE, atkbd->force_release_mask);
buf[len++] = '\n';
buf[len] = '\0';

View file

@ -370,7 +370,6 @@ static struct i2c_driver cap11xx_i2c_driver = {
module_i2c_driver(cap11xx_i2c_driver);
MODULE_ALIAS("platform:cap11xx");
MODULE_DESCRIPTION("Microchip CAP11XX driver");
MODULE_AUTHOR("Daniel Mack <[email protected]>");
MODULE_LICENSE("GPL v2");

View file

@ -190,7 +190,7 @@ static ssize_t gpio_keys_attr_show_helper(struct gpio_keys_drvdata *ddata,
__set_bit(bdata->button->code, bits);
}
ret = bitmap_scnlistprintf(buf, PAGE_SIZE - 2, bits, n_events);
ret = scnprintf(buf, PAGE_SIZE - 1, "%*pbl", n_events, bits);
buf[ret++] = '\n';
buf[ret] = '\0';

View file

@ -448,8 +448,7 @@ static int imx_keypad_probe(struct platform_device *pdev)
return -ENOMEM;
}
keypad = devm_kzalloc(&pdev->dev, sizeof(struct imx_keypad),
GFP_KERNEL);
keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL);
if (!keypad) {
dev_err(&pdev->dev, "not enough memory for driver data\n");
return -ENOMEM;

View file

@ -20,6 +20,7 @@
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
@ -28,10 +29,6 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <mach/hardware.h>
#include <linux/platform_data/keypad-pxa27x.h>
/*
* Keypad Controller registers
@ -348,13 +345,11 @@ static int pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad)
{
const struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
struct input_dev *input_dev = keypad->input_dev;
const struct matrix_keymap_data *keymap_data =
pdata ? pdata->matrix_keymap_data : NULL;
unsigned short keycode;
int i;
int error;
error = matrix_keypad_build_keymap(keymap_data, NULL,
error = matrix_keypad_build_keymap(pdata->matrix_keymap_data, NULL,
pdata->matrix_key_rows,
pdata->matrix_key_cols,
keypad->keycodes, input_dev);

View file

@ -0,0 +1,286 @@
/*
* Allwinner sun4i low res adc attached tablet keys driver
*
* Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/*
* Allwinnner sunxi SoCs have a lradc which is specifically designed to have
* various (tablet) keys (ie home, back, search, etc). attached to it using
* a resistor network. This driver is for the keys on such boards.
*
* There are 2 channels, currently this driver only supports channel 0 since
* there are no boards known to use channel 1.
*/
#include <linux/err.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#define LRADC_CTRL 0x00
#define LRADC_INTC 0x04
#define LRADC_INTS 0x08
#define LRADC_DATA0 0x0c
#define LRADC_DATA1 0x10
/* LRADC_CTRL bits */
#define FIRST_CONVERT_DLY(x) ((x) << 24) /* 8 bits */
#define CHAN_SELECT(x) ((x) << 22) /* 2 bits */
#define CONTINUE_TIME_SEL(x) ((x) << 16) /* 4 bits */
#define KEY_MODE_SEL(x) ((x) << 12) /* 2 bits */
#define LEVELA_B_CNT(x) ((x) << 8) /* 4 bits */
#define HOLD_EN(x) ((x) << 6)
#define LEVELB_VOL(x) ((x) << 4) /* 2 bits */
#define SAMPLE_RATE(x) ((x) << 2) /* 2 bits */
#define ENABLE(x) ((x) << 0)
/* LRADC_INTC and LRADC_INTS bits */
#define CHAN1_KEYUP_IRQ BIT(12)
#define CHAN1_ALRDY_HOLD_IRQ BIT(11)
#define CHAN1_HOLD_IRQ BIT(10)
#define CHAN1_KEYDOWN_IRQ BIT(9)
#define CHAN1_DATA_IRQ BIT(8)
#define CHAN0_KEYUP_IRQ BIT(4)
#define CHAN0_ALRDY_HOLD_IRQ BIT(3)
#define CHAN0_HOLD_IRQ BIT(2)
#define CHAN0_KEYDOWN_IRQ BIT(1)
#define CHAN0_DATA_IRQ BIT(0)
struct sun4i_lradc_keymap {
u32 voltage;
u32 keycode;
};
struct sun4i_lradc_data {
struct device *dev;
struct input_dev *input;
void __iomem *base;
struct regulator *vref_supply;
struct sun4i_lradc_keymap *chan0_map;
u32 chan0_map_count;
u32 chan0_keycode;
u32 vref;
};
static irqreturn_t sun4i_lradc_irq(int irq, void *dev_id)
{
struct sun4i_lradc_data *lradc = dev_id;
u32 i, ints, val, voltage, diff, keycode = 0, closest = 0xffffffff;
ints = readl(lradc->base + LRADC_INTS);
/*
* lradc supports only one keypress at a time, release does not give
* any info as to which key was released, so we cache the keycode.
*/
if (ints & CHAN0_KEYUP_IRQ) {
input_report_key(lradc->input, lradc->chan0_keycode, 0);
lradc->chan0_keycode = 0;
}
if ((ints & CHAN0_KEYDOWN_IRQ) && lradc->chan0_keycode == 0) {
val = readl(lradc->base + LRADC_DATA0) & 0x3f;
voltage = val * lradc->vref / 63;
for (i = 0; i < lradc->chan0_map_count; i++) {
diff = abs(lradc->chan0_map[i].voltage - voltage);
if (diff < closest) {
closest = diff;
keycode = lradc->chan0_map[i].keycode;
}
}
lradc->chan0_keycode = keycode;
input_report_key(lradc->input, lradc->chan0_keycode, 1);
}
input_sync(lradc->input);
writel(ints, lradc->base + LRADC_INTS);
return IRQ_HANDLED;
}
static int sun4i_lradc_open(struct input_dev *dev)
{
struct sun4i_lradc_data *lradc = input_get_drvdata(dev);
int error;
error = regulator_enable(lradc->vref_supply);
if (error)
return error;
/* lradc Vref internally is divided by 2/3 */
lradc->vref = regulator_get_voltage(lradc->vref_supply) * 2 / 3;
/*
* Set sample time to 4 ms / 250 Hz. Wait 2 * 4 ms for key to
* stabilize on press, wait (1 + 1) * 4 ms for key release
*/
writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) |
SAMPLE_RATE(0) | ENABLE(1), lradc->base + LRADC_CTRL);
writel(CHAN0_KEYUP_IRQ | CHAN0_KEYDOWN_IRQ, lradc->base + LRADC_INTC);
return 0;
}
static void sun4i_lradc_close(struct input_dev *dev)
{
struct sun4i_lradc_data *lradc = input_get_drvdata(dev);
/* Disable lradc, leave other settings unchanged */
writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) |
SAMPLE_RATE(2), lradc->base + LRADC_CTRL);
writel(0, lradc->base + LRADC_INTC);
regulator_disable(lradc->vref_supply);
}
static int sun4i_lradc_load_dt_keymap(struct device *dev,
struct sun4i_lradc_data *lradc)
{
struct device_node *np, *pp;
int i;
int error;
np = dev->of_node;
if (!np)
return -EINVAL;
lradc->chan0_map_count = of_get_child_count(np);
if (lradc->chan0_map_count == 0) {
dev_err(dev, "keymap is missing in device tree\n");
return -EINVAL;
}
lradc->chan0_map = devm_kmalloc_array(dev, lradc->chan0_map_count,
sizeof(struct sun4i_lradc_keymap),
GFP_KERNEL);
if (!lradc->chan0_map)
return -ENOMEM;
i = 0;
for_each_child_of_node(np, pp) {
struct sun4i_lradc_keymap *map = &lradc->chan0_map[i];
u32 channel;
error = of_property_read_u32(pp, "channel", &channel);
if (error || channel != 0) {
dev_err(dev, "%s: Inval channel prop\n", pp->name);
return -EINVAL;
}
error = of_property_read_u32(pp, "voltage", &map->voltage);
if (error) {
dev_err(dev, "%s: Inval voltage prop\n", pp->name);
return -EINVAL;
}
error = of_property_read_u32(pp, "linux,code", &map->keycode);
if (error) {
dev_err(dev, "%s: Inval linux,code prop\n", pp->name);
return -EINVAL;
}
i++;
}
return 0;
}
static int sun4i_lradc_probe(struct platform_device *pdev)
{
struct sun4i_lradc_data *lradc;
struct device *dev = &pdev->dev;
int i;
int error;
lradc = devm_kzalloc(dev, sizeof(struct sun4i_lradc_data), GFP_KERNEL);
if (!lradc)
return -ENOMEM;
error = sun4i_lradc_load_dt_keymap(dev, lradc);
if (error)
return error;
lradc->vref_supply = devm_regulator_get(dev, "vref");
if (IS_ERR(lradc->vref_supply))
return PTR_ERR(lradc->vref_supply);
lradc->dev = dev;
lradc->input = devm_input_allocate_device(dev);
if (!lradc->input)
return -ENOMEM;
lradc->input->name = pdev->name;
lradc->input->phys = "sun4i_lradc/input0";
lradc->input->open = sun4i_lradc_open;
lradc->input->close = sun4i_lradc_close;
lradc->input->id.bustype = BUS_HOST;
lradc->input->id.vendor = 0x0001;
lradc->input->id.product = 0x0001;
lradc->input->id.version = 0x0100;
__set_bit(EV_KEY, lradc->input->evbit);
for (i = 0; i < lradc->chan0_map_count; i++)
__set_bit(lradc->chan0_map[i].keycode, lradc->input->keybit);
input_set_drvdata(lradc->input, lradc);
lradc->base = devm_ioremap_resource(dev,
platform_get_resource(pdev, IORESOURCE_MEM, 0));
if (IS_ERR(lradc->base))
return PTR_ERR(lradc->base);
error = devm_request_irq(dev, platform_get_irq(pdev, 0),
sun4i_lradc_irq, 0,
"sun4i-a10-lradc-keys", lradc);
if (error)
return error;
error = input_register_device(lradc->input);
if (error)
return error;
platform_set_drvdata(pdev, lradc);
return 0;
}
static const struct of_device_id sun4i_lradc_of_match[] = {
{ .compatible = "allwinner,sun4i-a10-lradc-keys", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sun4i_lradc_of_match);
static struct platform_driver sun4i_lradc_driver = {
.driver = {
.name = "sun4i-a10-lradc-keys",
.of_match_table = of_match_ptr(sun4i_lradc_of_match),
},
.probe = sun4i_lradc_probe,
};
module_platform_driver(sun4i_lradc_driver);
MODULE_DESCRIPTION("Allwinner sun4i low res adc attached tablet keys driver");
MODULE_AUTHOR("Hans de Goede <[email protected]>");
MODULE_LICENSE("GPL");

View file

@ -93,6 +93,16 @@ config INPUT_BMA150
To compile this driver as a module, choose M here: the
module will be called bma150.
config INPUT_E3X0_BUTTON
tristate "NI Ettus Research USRP E3x0 Button support."
default n
help
Say Y here to enable support for the NI Ettus Research
USRP E3x0 Button.
To compile this driver as a module, choose M here: the
module will be called e3x0_button.
config INPUT_PCSPKR
tristate "PC Speaker support"
depends on PCSPKR_PLATFORM
@ -394,6 +404,18 @@ config INPUT_CM109
To compile this driver as a module, choose M here: the module will be
called cm109.
config INPUT_REGULATOR_HAPTIC
tristate "Regulator haptics support"
depends on REGULATOR
select INPUT_FF_MEMLESS
help
This option enables device driver support for the haptic controlled
by a regulator. This driver supports ff-memless interface
from input framework.
To compile this driver as a module, choose M here: the
module will be called regulator-haptic.
config INPUT_RETU_PWRBUTTON
tristate "Retu Power button Driver"
depends on MFD_RETU
@ -404,6 +426,27 @@ config INPUT_RETU_PWRBUTTON
To compile this driver as a module, choose M here. The module will
be called retu-pwrbutton.
config INPUT_TPS65218_PWRBUTTON
tristate "TPS65218 Power button driver"
depends on MFD_TPS65218
help
Say Y here if you want to enable power buttong reporting for
the TPS65218 Power Management IC device.
To compile this driver as a module, choose M here. The module will
be called tps65218-pwrbutton.
config INPUT_AXP20X_PEK
tristate "X-Powers AXP20X power button driver"
depends on MFD_AXP20X
help
Say Y here if you want to enable power key reporting via the
AXP20X PMIC.
To compile this driver as a module, choose M here. The module will
be called axp20x-pek.
config INPUT_TWL4030_PWRBUTTON
tristate "TWL4030 Power button Driver"
depends on TWL4030_CORE

View file

@ -26,6 +26,7 @@ obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o
obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o
obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
obj-$(CONFIG_INPUT_E3X0_BUTTON) += e3x0-button.o
obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o
obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o
obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o
@ -53,12 +54,15 @@ obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o
obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o
obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o
obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o
obj-$(CONFIG_INPUT_AXP20X_PEK) += axp20x-pek.o
obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o
obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY) += soc_button_array.o
obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
obj-$(CONFIG_INPUT_TPS65218_PWRBUTTON) += tps65218-pwrbutton.o
obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o
obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o
obj-$(CONFIG_INPUT_TWL6040_VIBRA) += twl6040-vibra.o

View file

@ -0,0 +1,290 @@
/*
* axp20x power button driver.
*
* Copyright (C) 2013 Carlo Caione <carlo@caione.org>
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/errno.h>
#include <linux/irq.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mfd/axp20x.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#define AXP20X_PEK_STARTUP_MASK (0xc0)
#define AXP20X_PEK_SHUTDOWN_MASK (0x03)
struct axp20x_pek {
struct axp20x_dev *axp20x;
struct input_dev *input;
int irq_dbr;
int irq_dbf;
};
struct axp20x_time {
unsigned int time;
unsigned int idx;
};
static const struct axp20x_time startup_time[] = {
{ .time = 128, .idx = 0 },
{ .time = 1000, .idx = 2 },
{ .time = 3000, .idx = 1 },
{ .time = 2000, .idx = 3 },
};
static const struct axp20x_time shutdown_time[] = {
{ .time = 4000, .idx = 0 },
{ .time = 6000, .idx = 1 },
{ .time = 8000, .idx = 2 },
{ .time = 10000, .idx = 3 },
};
struct axp20x_pek_ext_attr {
const struct axp20x_time *p_time;
unsigned int mask;
};
static struct axp20x_pek_ext_attr axp20x_pek_startup_ext_attr = {
.p_time = startup_time,
.mask = AXP20X_PEK_STARTUP_MASK,
};
static struct axp20x_pek_ext_attr axp20x_pek_shutdown_ext_attr = {
.p_time = shutdown_time,
.mask = AXP20X_PEK_SHUTDOWN_MASK,
};
static struct axp20x_pek_ext_attr *get_axp_ext_attr(struct device_attribute *attr)
{
return container_of(attr, struct dev_ext_attribute, attr)->var;
}
static ssize_t axp20x_show_ext_attr(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
struct axp20x_pek_ext_attr *axp20x_ea = get_axp_ext_attr(attr);
unsigned int val;
int ret, i;
ret = regmap_read(axp20x_pek->axp20x->regmap, AXP20X_PEK_KEY, &val);
if (ret != 0)
return ret;
val &= axp20x_ea->mask;
val >>= ffs(axp20x_ea->mask) - 1;
for (i = 0; i < 4; i++)
if (val == axp20x_ea->p_time[i].idx)
val = axp20x_ea->p_time[i].time;
return sprintf(buf, "%u\n", val);
}
static ssize_t axp20x_store_ext_attr(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
struct axp20x_pek_ext_attr *axp20x_ea = get_axp_ext_attr(attr);
char val_str[20];
size_t len;
int ret, i;
unsigned int val, idx = 0;
unsigned int best_err = UINT_MAX;
val_str[sizeof(val_str) - 1] = '\0';
strncpy(val_str, buf, sizeof(val_str) - 1);
len = strlen(val_str);
if (len && val_str[len - 1] == '\n')
val_str[len - 1] = '\0';
ret = kstrtouint(val_str, 10, &val);
if (ret)
return ret;
for (i = 3; i >= 0; i--) {
unsigned int err;
err = abs(axp20x_ea->p_time[i].time - val);
if (err < best_err) {
best_err = err;
idx = axp20x_ea->p_time[i].idx;
}
if (!err)
break;
}
idx <<= ffs(axp20x_ea->mask) - 1;
ret = regmap_update_bits(axp20x_pek->axp20x->regmap,
AXP20X_PEK_KEY,
axp20x_ea->mask, idx);
if (ret != 0)
return -EINVAL;
return count;
}
static struct dev_ext_attribute axp20x_dev_attr_startup = {
.attr = __ATTR(startup, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr),
.var = &axp20x_pek_startup_ext_attr,
};
static struct dev_ext_attribute axp20x_dev_attr_shutdown = {
.attr = __ATTR(shutdown, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr),
.var = &axp20x_pek_shutdown_ext_attr,
};
static struct attribute *axp20x_attributes[] = {
&axp20x_dev_attr_startup.attr.attr,
&axp20x_dev_attr_shutdown.attr.attr,
NULL,
};
static const struct attribute_group axp20x_attribute_group = {
.attrs = axp20x_attributes,
};
static irqreturn_t axp20x_pek_irq(int irq, void *pwr)
{
struct input_dev *idev = pwr;
struct axp20x_pek *axp20x_pek = input_get_drvdata(idev);
if (irq == axp20x_pek->irq_dbr)
input_report_key(idev, KEY_POWER, true);
else if (irq == axp20x_pek->irq_dbf)
input_report_key(idev, KEY_POWER, false);
input_sync(idev);
return IRQ_HANDLED;
}
static void axp20x_remove_sysfs_group(void *_data)
{
struct device *dev = _data;
sysfs_remove_group(&dev->kobj, &axp20x_attribute_group);
}
static int axp20x_pek_probe(struct platform_device *pdev)
{
struct axp20x_pek *axp20x_pek;
struct axp20x_dev *axp20x;
struct input_dev *idev;
int error;
axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek),
GFP_KERNEL);
if (!axp20x_pek)
return -ENOMEM;
axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent);
axp20x = axp20x_pek->axp20x;
axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR");
if (axp20x_pek->irq_dbr < 0) {
dev_err(&pdev->dev, "No IRQ for PEK_DBR, error=%d\n",
axp20x_pek->irq_dbr);
return axp20x_pek->irq_dbr;
}
axp20x_pek->irq_dbr = regmap_irq_get_virq(axp20x->regmap_irqc,
axp20x_pek->irq_dbr);
axp20x_pek->irq_dbf = platform_get_irq_byname(pdev, "PEK_DBF");
if (axp20x_pek->irq_dbf < 0) {
dev_err(&pdev->dev, "No IRQ for PEK_DBF, error=%d\n",
axp20x_pek->irq_dbf);
return axp20x_pek->irq_dbf;
}
axp20x_pek->irq_dbf = regmap_irq_get_virq(axp20x->regmap_irqc,
axp20x_pek->irq_dbf);
axp20x_pek->input = devm_input_allocate_device(&pdev->dev);
if (!axp20x_pek->input)
return -ENOMEM;
idev = axp20x_pek->input;
idev->name = "axp20x-pek";
idev->phys = "m1kbd/input2";
idev->dev.parent = &pdev->dev;
input_set_capability(idev, EV_KEY, KEY_POWER);
input_set_drvdata(idev, axp20x_pek);
error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbr,
axp20x_pek_irq, 0,
"axp20x-pek-dbr", idev);
if (error < 0) {
dev_err(axp20x->dev, "Failed to request dbr IRQ#%d: %d\n",
axp20x_pek->irq_dbr, error);
return error;
}
error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbf,
axp20x_pek_irq, 0,
"axp20x-pek-dbf", idev);
if (error < 0) {
dev_err(axp20x->dev, "Failed to request dbf IRQ#%d: %d\n",
axp20x_pek->irq_dbf, error);
return error;
}
error = sysfs_create_group(&pdev->dev.kobj, &axp20x_attribute_group);
if (error) {
dev_err(axp20x->dev, "Failed to create sysfs attributes: %d\n",
error);
return error;
}
error = devm_add_action(&pdev->dev,
axp20x_remove_sysfs_group, &pdev->dev);
if (error) {
axp20x_remove_sysfs_group(&pdev->dev);
dev_err(&pdev->dev, "Failed to add sysfs cleanup action: %d\n",
error);
return error;
}
error = input_register_device(idev);
if (error) {
dev_err(axp20x->dev, "Can't register input device: %d\n",
error);
return error;
}
platform_set_drvdata(pdev, axp20x_pek);
return 0;
}
static struct platform_driver axp20x_pek_driver = {
.probe = axp20x_pek_probe,
.driver = {
.name = "axp20x-pek",
},
};
module_platform_driver(axp20x_pek_driver);
MODULE_DESCRIPTION("axp20x Power Button");
MODULE_AUTHOR("Carlo Caione <[email protected]>");
MODULE_LICENSE("GPL");

View file

@ -7,29 +7,37 @@
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/platform_data/bfin_rotary.h>
#include <asm/portmux.h>
#include <asm/bfin_rotary.h>
static const u16 per_cnt[] = {
P_CNT_CUD,
P_CNT_CDG,
P_CNT_CZM,
0
};
#define CNT_CONFIG_OFF 0 /* CNT Config Offset */
#define CNT_IMASK_OFF 4 /* CNT Interrupt Mask Offset */
#define CNT_STATUS_OFF 8 /* CNT Status Offset */
#define CNT_COMMAND_OFF 12 /* CNT Command Offset */
#define CNT_DEBOUNCE_OFF 16 /* CNT Debounce Offset */
#define CNT_COUNTER_OFF 20 /* CNT Counter Offset */
#define CNT_MAX_OFF 24 /* CNT Maximum Count Offset */
#define CNT_MIN_OFF 28 /* CNT Minimum Count Offset */
struct bfin_rot {
struct input_dev *input;
void __iomem *base;
int irq;
unsigned int up_key;
unsigned int down_key;
unsigned int button_key;
unsigned int rel_code;
unsigned short mode;
unsigned short debounce;
unsigned short cnt_config;
unsigned short cnt_imask;
unsigned short cnt_debounce;
@ -59,18 +67,17 @@ static void report_rotary_event(struct bfin_rot *rotary, int delta)
static irqreturn_t bfin_rotary_isr(int irq, void *dev_id)
{
struct platform_device *pdev = dev_id;
struct bfin_rot *rotary = platform_get_drvdata(pdev);
struct bfin_rot *rotary = dev_id;
int delta;
switch (bfin_read_CNT_STATUS()) {
switch (readw(rotary->base + CNT_STATUS_OFF)) {
case ICII:
break;
case UCII:
case DCII:
delta = bfin_read_CNT_COUNTER();
delta = readl(rotary->base + CNT_COUNTER_OFF);
if (delta)
report_rotary_event(rotary, delta);
break;
@ -83,16 +90,52 @@ static irqreturn_t bfin_rotary_isr(int irq, void *dev_id)
break;
}
bfin_write_CNT_COMMAND(W1LCNT_ZERO); /* Clear COUNTER */
bfin_write_CNT_STATUS(-1); /* Clear STATUS */
writew(W1LCNT_ZERO, rotary->base + CNT_COMMAND_OFF); /* Clear COUNTER */
writew(-1, rotary->base + CNT_STATUS_OFF); /* Clear STATUS */
return IRQ_HANDLED;
}
static int bfin_rotary_open(struct input_dev *input)
{
struct bfin_rot *rotary = input_get_drvdata(input);
unsigned short val;
if (rotary->mode & ROT_DEBE)
writew(rotary->debounce & DPRESCALE,
rotary->base + CNT_DEBOUNCE_OFF);
writew(rotary->mode & ~CNTE, rotary->base + CNT_CONFIG_OFF);
val = UCIE | DCIE;
if (rotary->button_key)
val |= CZMIE;
writew(val, rotary->base + CNT_IMASK_OFF);
writew(rotary->mode | CNTE, rotary->base + CNT_CONFIG_OFF);
return 0;
}
static void bfin_rotary_close(struct input_dev *input)
{
struct bfin_rot *rotary = input_get_drvdata(input);
writew(0, rotary->base + CNT_CONFIG_OFF);
writew(0, rotary->base + CNT_IMASK_OFF);
}
static void bfin_rotary_free_action(void *data)
{
peripheral_free_list(data);
}
static int bfin_rotary_probe(struct platform_device *pdev)
{
struct bfin_rotary_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct device *dev = &pdev->dev;
const struct bfin_rotary_platform_data *pdata = dev_get_platdata(dev);
struct bfin_rot *rotary;
struct resource *res;
struct input_dev *input;
int error;
@ -102,18 +145,37 @@ static int bfin_rotary_probe(struct platform_device *pdev)
return -EINVAL;
}
error = peripheral_request_list(per_cnt, dev_name(&pdev->dev));
if (error) {
dev_err(&pdev->dev, "requesting peripherals failed\n");
return error;
if (pdata->pin_list) {
error = peripheral_request_list(pdata->pin_list,
dev_name(&pdev->dev));
if (error) {
dev_err(dev, "requesting peripherals failed: %d\n",
error);
return error;
}
error = devm_add_action(dev, bfin_rotary_free_action,
pdata->pin_list);
if (error) {
dev_err(dev, "setting cleanup action failed: %d\n",
error);
peripheral_free_list(pdata->pin_list);
return error;
}
}
rotary = kzalloc(sizeof(struct bfin_rot), GFP_KERNEL);
input = input_allocate_device();
if (!rotary || !input) {
error = -ENOMEM;
goto out1;
}
rotary = devm_kzalloc(dev, sizeof(struct bfin_rot), GFP_KERNEL);
if (!rotary)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
rotary->base = devm_ioremap_resource(dev, res);
if (IS_ERR(rotary->base))
return PTR_ERR(rotary->base);
input = devm_input_allocate_device(dev);
if (!input)
return -ENOMEM;
rotary->input = input;
@ -122,9 +184,8 @@ static int bfin_rotary_probe(struct platform_device *pdev)
rotary->button_key = pdata->rotary_button_key;
rotary->rel_code = pdata->rotary_rel_code;
error = rotary->irq = platform_get_irq(pdev, 0);
if (error < 0)
goto out1;
rotary->mode = pdata->mode;
rotary->debounce = pdata->debounce;
input->name = pdev->name;
input->phys = "bfin-rotary/input0";
@ -137,6 +198,9 @@ static int bfin_rotary_probe(struct platform_device *pdev)
input->id.product = 0x0001;
input->id.version = 0x0100;
input->open = bfin_rotary_open;
input->close = bfin_rotary_close;
if (rotary->up_key) {
__set_bit(EV_KEY, input->evbit);
__set_bit(rotary->up_key, input->keybit);
@ -151,75 +215,43 @@ static int bfin_rotary_probe(struct platform_device *pdev)
__set_bit(rotary->button_key, input->keybit);
}
error = request_irq(rotary->irq, bfin_rotary_isr,
0, dev_name(&pdev->dev), pdev);
/* Quiesce the device before requesting irq */
bfin_rotary_close(input);
rotary->irq = platform_get_irq(pdev, 0);
if (rotary->irq < 0) {
dev_err(dev, "No rotary IRQ specified\n");
return -ENOENT;
}
error = devm_request_irq(dev, rotary->irq, bfin_rotary_isr,
0, dev_name(dev), rotary);
if (error) {
dev_err(&pdev->dev,
"unable to claim irq %d; error %d\n",
dev_err(dev, "unable to claim irq %d; error %d\n",
rotary->irq, error);
goto out1;
return error;
}
error = input_register_device(input);
if (error) {
dev_err(&pdev->dev,
"unable to register input device (%d)\n", error);
goto out2;
dev_err(dev, "unable to register input device (%d)\n", error);
return error;
}
if (pdata->rotary_button_key)
bfin_write_CNT_IMASK(CZMIE);
if (pdata->mode & ROT_DEBE)
bfin_write_CNT_DEBOUNCE(pdata->debounce & DPRESCALE);
if (pdata->mode)
bfin_write_CNT_CONFIG(bfin_read_CNT_CONFIG() |
(pdata->mode & ~CNTE));
bfin_write_CNT_IMASK(bfin_read_CNT_IMASK() | UCIE | DCIE);
bfin_write_CNT_CONFIG(bfin_read_CNT_CONFIG() | CNTE);
platform_set_drvdata(pdev, rotary);
device_init_wakeup(&pdev->dev, 1);
return 0;
out2:
free_irq(rotary->irq, pdev);
out1:
input_free_device(input);
kfree(rotary);
peripheral_free_list(per_cnt);
return error;
}
static int bfin_rotary_remove(struct platform_device *pdev)
{
struct bfin_rot *rotary = platform_get_drvdata(pdev);
bfin_write_CNT_CONFIG(0);
bfin_write_CNT_IMASK(0);
free_irq(rotary->irq, pdev);
input_unregister_device(rotary->input);
peripheral_free_list(per_cnt);
kfree(rotary);
return 0;
}
#ifdef CONFIG_PM
static int bfin_rotary_suspend(struct device *dev)
static int __maybe_unused bfin_rotary_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct bfin_rot *rotary = platform_get_drvdata(pdev);
rotary->cnt_config = bfin_read_CNT_CONFIG();
rotary->cnt_imask = bfin_read_CNT_IMASK();
rotary->cnt_debounce = bfin_read_CNT_DEBOUNCE();
rotary->cnt_config = readw(rotary->base + CNT_CONFIG_OFF);
rotary->cnt_imask = readw(rotary->base + CNT_IMASK_OFF);
rotary->cnt_debounce = readw(rotary->base + CNT_DEBOUNCE_OFF);
if (device_may_wakeup(&pdev->dev))
enable_irq_wake(rotary->irq);
@ -227,38 +259,32 @@ static int bfin_rotary_suspend(struct device *dev)
return 0;
}
static int bfin_rotary_resume(struct device *dev)
static int __maybe_unused bfin_rotary_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct bfin_rot *rotary = platform_get_drvdata(pdev);
bfin_write_CNT_DEBOUNCE(rotary->cnt_debounce);
bfin_write_CNT_IMASK(rotary->cnt_imask);
bfin_write_CNT_CONFIG(rotary->cnt_config & ~CNTE);
writew(rotary->cnt_debounce, rotary->base + CNT_DEBOUNCE_OFF);
writew(rotary->cnt_imask, rotary->base + CNT_IMASK_OFF);
writew(rotary->cnt_config & ~CNTE, rotary->base + CNT_CONFIG_OFF);
if (device_may_wakeup(&pdev->dev))
disable_irq_wake(rotary->irq);
if (rotary->cnt_config & CNTE)
bfin_write_CNT_CONFIG(rotary->cnt_config);
writew(rotary->cnt_config, rotary->base + CNT_CONFIG_OFF);
return 0;
}
static const struct dev_pm_ops bfin_rotary_pm_ops = {
.suspend = bfin_rotary_suspend,
.resume = bfin_rotary_resume,
};
#endif
static SIMPLE_DEV_PM_OPS(bfin_rotary_pm_ops,
bfin_rotary_suspend, bfin_rotary_resume);
static struct platform_driver bfin_rotary_device_driver = {
.probe = bfin_rotary_probe,
.remove = bfin_rotary_remove,
.driver = {
.name = "bfin-rotary",
#ifdef CONFIG_PM
.pm = &bfin_rotary_pm_ops,
#endif
},
};
module_platform_driver(bfin_rotary_device_driver);

View file

@ -733,7 +733,6 @@ static struct i2c_driver drv260x_driver = {
};
module_i2c_driver(drv260x_driver);
MODULE_ALIAS("platform:drv260x-haptics");
MODULE_DESCRIPTION("TI DRV260x haptics driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Dan Murphy <[email protected]>");

View file

@ -492,7 +492,6 @@ static struct i2c_driver drv2667_driver = {
};
module_i2c_driver(drv2667_driver);
MODULE_ALIAS("platform:drv2667-haptics");
MODULE_DESCRIPTION("TI DRV2667 haptics driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Dan Murphy <[email protected]>");

View file

@ -0,0 +1,157 @@
/*
* Copyright (c) 2014, National Instruments Corp. All rights reserved.
*
* Driver for NI Ettus Research USRP E3x0 Button Driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/slab.h>
static irqreturn_t e3x0_button_release_handler(int irq, void *data)
{
struct input_dev *idev = data;
input_report_key(idev, KEY_POWER, 0);
input_sync(idev);
return IRQ_HANDLED;
}
static irqreturn_t e3x0_button_press_handler(int irq, void *data)
{
struct input_dev *idev = data;
input_report_key(idev, KEY_POWER, 1);
pm_wakeup_event(idev->dev.parent, 0);
input_sync(idev);
return IRQ_HANDLED;
}
static int __maybe_unused e3x0_button_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
if (device_may_wakeup(dev))
enable_irq_wake(platform_get_irq_byname(pdev, "press"));
return 0;
}
static int __maybe_unused e3x0_button_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
if (device_may_wakeup(dev))
disable_irq_wake(platform_get_irq_byname(pdev, "press"));
return 0;
}
static SIMPLE_DEV_PM_OPS(e3x0_button_pm_ops,
e3x0_button_suspend, e3x0_button_resume);
static int e3x0_button_probe(struct platform_device *pdev)
{
struct input_dev *input;
int irq_press, irq_release;
int error;
irq_press = platform_get_irq_byname(pdev, "press");
if (irq_press < 0) {
dev_err(&pdev->dev, "No IRQ for 'press', error=%d\n",
irq_press);
return irq_press;
}
irq_release = platform_get_irq_byname(pdev, "release");
if (irq_release < 0) {
dev_err(&pdev->dev, "No IRQ for 'release', error=%d\n",
irq_release);
return irq_release;
}
input = devm_input_allocate_device(&pdev->dev);
if (!input)
return -ENOMEM;
input->name = "NI Ettus Research USRP E3x0 Button Driver";
input->phys = "e3x0_button/input0";
input->dev.parent = &pdev->dev;
input_set_capability(input, EV_KEY, KEY_POWER);
error = devm_request_irq(&pdev->dev, irq_press,
e3x0_button_press_handler, 0,
"e3x0-button", input);
if (error) {
dev_err(&pdev->dev, "Failed to request 'press' IRQ#%d: %d\n",
irq_press, error);
return error;
}
error = devm_request_irq(&pdev->dev, irq_release,
e3x0_button_release_handler, 0,
"e3x0-button", input);
if (error) {
dev_err(&pdev->dev, "Failed to request 'release' IRQ#%d: %d\n",
irq_release, error);
return error;
}
error = input_register_device(input);
if (error) {
dev_err(&pdev->dev, "Can't register input device: %d\n", error);
return error;
}
platform_set_drvdata(pdev, input);
device_init_wakeup(&pdev->dev, 1);
return 0;
}
static int e3x0_button_remove(struct platform_device *pdev)
{
device_init_wakeup(&pdev->dev, 0);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id e3x0_button_match[] = {
{ .compatible = "ettus,e3x0-button", },
{ }
};
MODULE_DEVICE_TABLE(of, e3x0_button_match);
#endif
static struct platform_driver e3x0_button_driver = {
.driver = {
.name = "e3x0-button",
.of_match_table = of_match_ptr(e3x0_button_match),
.pm = &e3x0_button_pm_ops,
},
.probe = e3x0_button_probe,
.remove = e3x0_button_remove,
};
module_platform_driver(e3x0_button_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Moritz Fischer <[email protected]>");
MODULE_DESCRIPTION("NI Ettus Research USRP E3x0 Button driver");
MODULE_ALIAS("platform:e3x0-button");

View file

@ -0,0 +1,266 @@
/*
* Regulator haptic driver
*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
* Author: Jaewon Kim <jaewon02.kim@samsung.com>
* Author: Hyunhee Kim <hyunhee.kim@samsung.com>
*
* 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/input.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_data/regulator-haptic.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#define MAX_MAGNITUDE_SHIFT 16
struct regulator_haptic {
struct device *dev;
struct input_dev *input_dev;
struct regulator *regulator;
struct work_struct work;
struct mutex mutex;
bool active;
bool suspended;
unsigned int max_volt;
unsigned int min_volt;
unsigned int magnitude;
};
static int regulator_haptic_toggle(struct regulator_haptic *haptic, bool on)
{
int error;
if (haptic->active != on) {
error = on ? regulator_enable(haptic->regulator) :
regulator_disable(haptic->regulator);
if (error) {
dev_err(haptic->dev,
"failed to switch regulator %s: %d\n",
on ? "on" : "off", error);
return error;
}
haptic->active = on;
}
return 0;
}
static int regulator_haptic_set_voltage(struct regulator_haptic *haptic,
unsigned int magnitude)
{
u64 volt_mag_multi;
unsigned int intensity;
int error;
volt_mag_multi = (u64)(haptic->max_volt - haptic->min_volt) * magnitude;
intensity = (unsigned int)(volt_mag_multi >> MAX_MAGNITUDE_SHIFT);
error = regulator_set_voltage(haptic->regulator,
intensity + haptic->min_volt,
haptic->max_volt);
if (error) {
dev_err(haptic->dev, "cannot set regulator voltage to %d: %d\n",
intensity + haptic->min_volt, error);
return error;
}
regulator_haptic_toggle(haptic, !!magnitude);
return 0;
}
static void regulator_haptic_work(struct work_struct *work)
{
struct regulator_haptic *haptic = container_of(work,
struct regulator_haptic, work);
mutex_lock(&haptic->mutex);
if (!haptic->suspended)
regulator_haptic_set_voltage(haptic, haptic->magnitude);
mutex_unlock(&haptic->mutex);
}
static int regulator_haptic_play_effect(struct input_dev *input, void *data,
struct ff_effect *effect)
{
struct regulator_haptic *haptic = input_get_drvdata(input);
haptic->magnitude = effect->u.rumble.strong_magnitude;
if (!haptic->magnitude)
haptic->magnitude = effect->u.rumble.weak_magnitude;
schedule_work(&haptic->work);
return 0;
}
static void regulator_haptic_close(struct input_dev *input)
{
struct regulator_haptic *haptic = input_get_drvdata(input);
cancel_work_sync(&haptic->work);
regulator_haptic_set_voltage(haptic, 0);
}
static int __maybe_unused
regulator_haptic_parse_dt(struct device *dev, struct regulator_haptic *haptic)
{
struct device_node *node;
int error;
node = dev->of_node;
if(!node) {
dev_err(dev, "Missing dveice tree data\n");
return -EINVAL;
}
error = of_property_read_u32(node, "max-microvolt", &haptic->max_volt);
if (error) {
dev_err(dev, "cannot parse max-microvolt\n");
return error;
}
error = of_property_read_u32(node, "min-microvolt", &haptic->min_volt);
if (error) {
dev_err(dev, "cannot parse min-microvolt\n");
return error;
}
return 0;
}
static int regulator_haptic_probe(struct platform_device *pdev)
{
const struct regulator_haptic_data *pdata = dev_get_platdata(&pdev->dev);
struct regulator_haptic *haptic;
struct input_dev *input_dev;
int error;
haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL);
if (!haptic)
return -ENOMEM;
platform_set_drvdata(pdev, haptic);
haptic->dev = &pdev->dev;
mutex_init(&haptic->mutex);
INIT_WORK(&haptic->work, regulator_haptic_work);
if (pdata) {
haptic->max_volt = pdata->max_volt;
haptic->min_volt = pdata->min_volt;
} else if (IS_ENABLED(CONFIG_OF)) {
error = regulator_haptic_parse_dt(&pdev->dev, haptic);
if (error)
return error;
} else {
dev_err(&pdev->dev, "Missing platform data\n");
return -EINVAL;
}
haptic->regulator = devm_regulator_get_exclusive(&pdev->dev, "haptic");
if (IS_ERR(haptic->regulator)) {
dev_err(&pdev->dev, "failed to get regulator\n");
return PTR_ERR(haptic->regulator);
}
input_dev = devm_input_allocate_device(&pdev->dev);
if (!input_dev)
return -ENOMEM;
haptic->input_dev = input_dev;
haptic->input_dev->name = "regulator-haptic";
haptic->input_dev->dev.parent = &pdev->dev;
haptic->input_dev->close = regulator_haptic_close;
input_set_drvdata(haptic->input_dev, haptic);
input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE);
error = input_ff_create_memless(input_dev, NULL,
regulator_haptic_play_effect);
if (error) {
dev_err(&pdev->dev, "failed to create force-feedback\n");
return error;
}
error = input_register_device(haptic->input_dev);
if (error) {
dev_err(&pdev->dev, "failed to register input device\n");
return error;
}
return 0;
}
static int __maybe_unused regulator_haptic_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct regulator_haptic *haptic = platform_get_drvdata(pdev);
int error;
error = mutex_lock_interruptible(&haptic->mutex);
if (error)
return error;
regulator_haptic_set_voltage(haptic, 0);
haptic->suspended = true;
mutex_unlock(&haptic->mutex);
return 0;
}
static int __maybe_unused regulator_haptic_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct regulator_haptic *haptic = platform_get_drvdata(pdev);
unsigned int magnitude;
mutex_lock(&haptic->mutex);
haptic->suspended = false;
magnitude = ACCESS_ONCE(haptic->magnitude);
if (magnitude)
regulator_haptic_set_voltage(haptic, magnitude);
mutex_unlock(&haptic->mutex);
return 0;
}
static SIMPLE_DEV_PM_OPS(regulator_haptic_pm_ops,
regulator_haptic_suspend, regulator_haptic_resume);
static struct of_device_id regulator_haptic_dt_match[] = {
{ .compatible = "regulator-haptic" },
{ /* sentinel */ },
};
static struct platform_driver regulator_haptic_driver = {
.probe = regulator_haptic_probe,
.driver = {
.name = "regulator-haptic",
.of_match_table = regulator_haptic_dt_match,
.pm = &regulator_haptic_pm_ops,
},
};
module_platform_driver(regulator_haptic_driver);
MODULE_AUTHOR("Jaewon Kim <[email protected]>");
MODULE_AUTHOR("Hyunhee Kim <[email protected]>");
MODULE_DESCRIPTION("Regulator haptic driver");
MODULE_LICENSE("GPL");

View file

@ -195,7 +195,7 @@ static int soc_button_probe(struct platform_device *pdev)
static struct soc_button_info soc_button_PNP0C40[] = {
{ "power", 0, EV_KEY, KEY_POWER, false, true },
{ "home", 1, EV_KEY, KEY_HOME, false, true },
{ "home", 1, EV_KEY, KEY_LEFTMETA, false, true },
{ "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false },
{ "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false },
{ "rotation_lock", 4, EV_SW, SW_ROTATE_LOCK, false, false },

View file

@ -0,0 +1,126 @@
/*
* Texas Instruments' TPS65218 Power Button Input Driver
*
* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
* Author: Felipe Balbi <balbi@ti.com>
*
* 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.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mfd/tps65218.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
struct tps65218_pwrbutton {
struct device *dev;
struct tps65218 *tps;
struct input_dev *idev;
};
static irqreturn_t tps65218_pwr_irq(int irq, void *_pwr)
{
struct tps65218_pwrbutton *pwr = _pwr;
unsigned int reg;
int error;
error = tps65218_reg_read(pwr->tps, TPS65218_REG_STATUS, &reg);
if (error) {
dev_err(pwr->dev, "can't read register: %d\n", error);
goto out;
}
if (reg & TPS65218_STATUS_PB_STATE) {
input_report_key(pwr->idev, KEY_POWER, 1);
pm_wakeup_event(pwr->dev, 0);
} else {
input_report_key(pwr->idev, KEY_POWER, 0);
}
input_sync(pwr->idev);
out:
return IRQ_HANDLED;
}
static int tps65218_pwron_probe(struct platform_device *pdev)
{
struct tps65218 *tps = dev_get_drvdata(pdev->dev.parent);
struct device *dev = &pdev->dev;
struct tps65218_pwrbutton *pwr;
struct input_dev *idev;
int error;
int irq;
pwr = devm_kzalloc(dev, sizeof(*pwr), GFP_KERNEL);
if (!pwr)
return -ENOMEM;
idev = devm_input_allocate_device(dev);
if (!idev)
return -ENOMEM;
idev->name = "tps65218_pwrbutton";
idev->phys = "tps65218_pwrbutton/input0";
idev->dev.parent = dev;
idev->id.bustype = BUS_I2C;
input_set_capability(idev, EV_KEY, KEY_POWER);
pwr->tps = tps;
pwr->dev = dev;
pwr->idev = idev;
platform_set_drvdata(pdev, pwr);
device_init_wakeup(dev, true);
irq = platform_get_irq(pdev, 0);
error = devm_request_threaded_irq(dev, irq, NULL, tps65218_pwr_irq,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
"tps65218-pwrbutton", pwr);
if (error) {
dev_err(dev, "failed to request IRQ #%d: %d\n",
irq, error);
return error;
}
error= input_register_device(idev);
if (error) {
dev_err(dev, "Can't register power button: %d\n", error);
return error;
}
return 0;
}
static struct of_device_id of_tps65218_pwr_match[] = {
{ .compatible = "ti,tps65218-pwrbutton" },
{ },
};
MODULE_DEVICE_TABLE(of, of_tps65218_pwr_match);
static struct platform_driver tps65218_pwron_driver = {
.probe = tps65218_pwron_probe,
.driver = {
.name = "tps65218_pwrbutton",
.of_match_table = of_tps65218_pwr_match,
},
};
module_platform_driver(tps65218_pwron_driver);
MODULE_DESCRIPTION("TPS65218 Power Button");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Felipe Balbi <[email protected]>");

View file

@ -105,19 +105,12 @@ config MOUSE_PS2_ELANTECH
Say Y here if you have an Elantech PS/2 touchpad connected
to your system.
Note that if you enable this driver you will need an updated
X.org Synaptics driver that does not require ABS_PRESSURE
reports from the touchpad (i.e. post 1.5.0 version). You can
grab a patch for the driver here:
http://userweb.kernel.org/~dtor/synaptics-no-abspressure.patch
If unsure, say N.
This driver exposes some configuration registers via sysfs
entries. For further information,
see <file:Documentation/input/elantech.txt>.
If unsure, say N.
config MOUSE_PS2_SENTELIC
bool "Sentelic Finger Sensing Pad PS/2 protocol extension"
depends on MOUSE_PS2
@ -146,6 +139,16 @@ config MOUSE_PS2_OLPC
If unsure, say N.
config MOUSE_PS2_FOCALTECH
bool "FocalTech PS/2 mouse protocol extension" if EXPERT
default y
depends on MOUSE_PS2
help
Say Y here if you have a FocalTech PS/2 TouchPad connected to
your system.
If unsure, say Y.
config MOUSE_SERIAL
tristate "Serial mouse"
select SERIO
@ -206,6 +209,7 @@ config MOUSE_BCM5974
config MOUSE_CYAPA
tristate "Cypress APA I2C Trackpad support"
depends on I2C
select CRC_ITU_T
help
This driver adds support for Cypress All Points Addressable (APA)
I2C Trackpads, including the ones used in 2012 Samsung Chromebooks.

View file

@ -8,7 +8,7 @@ obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o
obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o
obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o
obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o
obj-$(CONFIG_MOUSE_CYAPA) += cyapa.o
obj-$(CONFIG_MOUSE_CYAPA) += cyapatp.o
obj-$(CONFIG_MOUSE_ELAN_I2C) += elan_i2c.o
obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
obj-$(CONFIG_MOUSE_INPORT) += inport.o
@ -24,6 +24,7 @@ obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o
obj-$(CONFIG_MOUSE_SYNAPTICS_USB) += synaptics_usb.o
obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
cyapatp-objs := cyapa.o cyapa_gen3.o cyapa_gen5.o
psmouse-objs := psmouse-base.o synaptics.o focaltech.o
psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o

View file

@ -99,36 +99,58 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = {
#define ALPS_FOUR_BUTTONS 0x40 /* 4 direction button present */
#define ALPS_PS2_INTERLEAVED 0x80 /* 3-byte PS/2 packet interleaved with
6-byte ALPS packet */
#define ALPS_IS_RUSHMORE 0x100 /* device is a rushmore */
#define ALPS_BUTTONPAD 0x200 /* device is a clickpad */
static const struct alps_model_info alps_model_data[] = {
{ { 0x32, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */
{ { 0x33, 0x02, 0x0a }, 0x00, ALPS_PROTO_V1, 0x88, 0xf8, 0 }, /* UMAX-530T */
{ { 0x53, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },
{ { 0x53, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },
{ { 0x60, 0x03, 0xc8 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, /* HP ze1115 */
{ { 0x63, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },
{ { 0x63, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },
{ { 0x63, 0x02, 0x28 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Fujitsu Siemens S6010 */
{ { 0x63, 0x02, 0x3c }, 0x00, ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL }, /* Toshiba Satellite S2400-103 */
{ { 0x63, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */
{ { 0x63, 0x02, 0x64 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },
{ { 0x63, 0x03, 0xc8 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D800 */
{ { 0x73, 0x00, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT }, /* ThinkPad R61 8918-5QG */
{ { 0x73, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },
{ { 0x73, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */
{ { 0x20, 0x02, 0x0e }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */
{ { 0x22, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },
{ { 0x22, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */
{ { 0x32, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, /* Toshiba Salellite Pro M10 */
{ { 0x33, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V1, 0x88, 0xf8, 0 } }, /* UMAX-530T */
{ { 0x53, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
{ { 0x53, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
{ { 0x60, 0x03, 0xc8 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, /* HP ze1115 */
{ { 0x63, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
{ { 0x63, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
{ { 0x63, 0x02, 0x28 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } }, /* Fujitsu Siemens S6010 */
{ { 0x63, 0x02, 0x3c }, 0x00, { ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL } }, /* Toshiba Satellite S2400-103 */
{ { 0x63, 0x02, 0x50 }, 0x00, { ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 } }, /* NEC Versa L320 */
{ { 0x63, 0x02, 0x64 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
{ { 0x63, 0x03, 0xc8 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, /* Dell Latitude D800 */
{ { 0x73, 0x00, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT } }, /* ThinkPad R61 8918-5QG */
{ { 0x73, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
{ { 0x73, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } }, /* Ahtec Laptop */
/*
* XXX This entry is suspicious. First byte has zero lower nibble,
* which is what a normal mouse would report. Also, the value 0x0e
* isn't valid per PS/2 spec.
*/
{ { 0x20, 0x02, 0x0e }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },
{ { 0x22, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },
{ { 0x22, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT } }, /* Dell Latitude D600 */
/* Dell Latitude E5500, E6400, E6500, Precision M4400 */
{ { 0x62, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf,
ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED },
{ { 0x73, 0x00, 0x14 }, 0x00, ALPS_PROTO_V6, 0xff, 0xff, ALPS_DUALPOINT }, /* Dell XT2 */
{ { 0x73, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */
{ { 0x52, 0x01, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff,
ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, /* Toshiba Tecra A11-11L */
{ { 0x73, 0x02, 0x64 }, 0x8a, ALPS_PROTO_V4, 0x8f, 0x8f, 0 },
{ { 0x62, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xcf, 0xcf,
ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED } },
{ { 0x73, 0x00, 0x14 }, 0x00, { ALPS_PROTO_V6, 0xff, 0xff, ALPS_DUALPOINT } }, /* Dell XT2 */
{ { 0x73, 0x02, 0x50 }, 0x00, { ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS } }, /* Dell Vostro 1400 */
{ { 0x52, 0x01, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xff, 0xff,
ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED } }, /* Toshiba Tecra A11-11L */
{ { 0x73, 0x02, 0x64 }, 0x8a, { ALPS_PROTO_V4, 0x8f, 0x8f, 0 } },
};
static const struct alps_protocol_info alps_v3_protocol_data = {
ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT
};
static const struct alps_protocol_info alps_v3_rushmore_data = {
ALPS_PROTO_V3_RUSHMORE, 0x8f, 0x8f, ALPS_DUALPOINT
};
static const struct alps_protocol_info alps_v5_protocol_data = {
ALPS_PROTO_V5, 0xc8, 0xd8, 0
};
static const struct alps_protocol_info alps_v7_protocol_data = {
ALPS_PROTO_V7, 0x48, 0x48, ALPS_DUALPOINT
};
static void alps_set_abs_params_st(struct alps_data *priv,
@ -136,12 +158,6 @@ static void alps_set_abs_params_st(struct alps_data *priv,
static void alps_set_abs_params_mt(struct alps_data *priv,
struct input_dev *dev1);
/*
* XXX - this entry is suspicious. First byte has zero lower nibble,
* which is what a normal mouse would report. Also, the value 0x0e
* isn't valid per PS/2 spec.
*/
/* Packet formats are described in Documentation/input/alps.txt */
static bool alps_is_valid_first_byte(struct alps_data *priv,
@ -150,8 +166,7 @@ static bool alps_is_valid_first_byte(struct alps_data *priv,
return (data & priv->mask0) == priv->byte0;
}
static void alps_report_buttons(struct psmouse *psmouse,
struct input_dev *dev1, struct input_dev *dev2,
static void alps_report_buttons(struct input_dev *dev1, struct input_dev *dev2,
int left, int right, int middle)
{
struct input_dev *dev;
@ -161,20 +176,21 @@ static void alps_report_buttons(struct psmouse *psmouse,
* other device (dev2) then this event should be also
* sent through that device.
*/
dev = test_bit(BTN_LEFT, dev2->key) ? dev2 : dev1;
dev = (dev2 && test_bit(BTN_LEFT, dev2->key)) ? dev2 : dev1;
input_report_key(dev, BTN_LEFT, left);
dev = test_bit(BTN_RIGHT, dev2->key) ? dev2 : dev1;
dev = (dev2 && test_bit(BTN_RIGHT, dev2->key)) ? dev2 : dev1;
input_report_key(dev, BTN_RIGHT, right);
dev = test_bit(BTN_MIDDLE, dev2->key) ? dev2 : dev1;
dev = (dev2 && test_bit(BTN_MIDDLE, dev2->key)) ? dev2 : dev1;
input_report_key(dev, BTN_MIDDLE, middle);
/*
* Sync the _other_ device now, we'll do the first
* device later once we report the rest of the events.
*/
input_sync(dev2);
if (dev2)
input_sync(dev2);
}
static void alps_process_packet_v1_v2(struct psmouse *psmouse)
@ -221,13 +237,13 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
input_report_rel(dev2, REL_X, (x > 383 ? (x - 768) : x));
input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y));
alps_report_buttons(psmouse, dev2, dev, left, right, middle);
alps_report_buttons(dev2, dev, left, right, middle);
input_sync(dev2);
return;
}
alps_report_buttons(psmouse, dev, dev2, left, right, middle);
alps_report_buttons(dev, dev2, left, right, middle);
/* Convert hardware tap to a reasonable Z value */
if (ges && !fin)
@ -412,7 +428,7 @@ static int alps_process_bitmap(struct alps_data *priv,
(2 * (priv->y_bits - 1));
/* y-bitmap order is reversed, except on rushmore */
if (!(priv->flags & ALPS_IS_RUSHMORE)) {
if (priv->proto_version != ALPS_PROTO_V3_RUSHMORE) {
fields->mt[0].y = priv->y_max - fields->mt[0].y;
fields->mt[1].y = priv->y_max - fields->mt[1].y;
}
@ -435,7 +451,7 @@ static void alps_report_mt_data(struct psmouse *psmouse, int n)
struct alps_fields *f = &priv->f;
int i, slot[MAX_TOUCHES];
input_mt_assign_slots(dev, slot, f->mt, n);
input_mt_assign_slots(dev, slot, f->mt, n, 0);
for (i = 0; i < n; i++)
alps_set_slot(dev, slot[i], f->mt[i].x, f->mt[i].y);
@ -475,6 +491,13 @@ static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
struct input_dev *dev = priv->dev2;
int x, y, z, left, right, middle;
/* It should be a DualPoint when received trackstick packet */
if (!(priv->flags & ALPS_DUALPOINT)) {
psmouse_warn(psmouse,
"Rejected trackstick packet from non DualPoint device");
return;
}
/* Sanity check packet */
if (!(packet[0] & 0x40)) {
psmouse_dbg(psmouse, "Bad trackstick packet, discarding\n");
@ -641,7 +664,8 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
*/
if (f->is_mp) {
fingers = f->fingers;
if (priv->proto_version == ALPS_PROTO_V3) {
if (priv->proto_version == ALPS_PROTO_V3 ||
priv->proto_version == ALPS_PROTO_V3_RUSHMORE) {
if (alps_process_bitmap(priv, f) == 0)
fingers = 0; /* Use st data */
@ -699,7 +723,8 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
alps_report_semi_mt_data(psmouse, fingers);
if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) {
if ((priv->flags & ALPS_DUALPOINT) &&
!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) {
input_report_key(dev2, BTN_LEFT, f->ts_left);
input_report_key(dev2, BTN_RIGHT, f->ts_right);
input_report_key(dev2, BTN_MIDDLE, f->ts_middle);
@ -743,8 +768,11 @@ static void alps_process_packet_v6(struct psmouse *psmouse)
*/
if (packet[5] == 0x7F) {
/* It should be a DualPoint when received Trackpoint packet */
if (!(priv->flags & ALPS_DUALPOINT))
if (!(priv->flags & ALPS_DUALPOINT)) {
psmouse_warn(psmouse,
"Rejected trackstick packet from non DualPoint device");
return;
}
/* Trackpoint packet */
x = packet[1] | ((packet[3] & 0x20) << 2);
@ -881,34 +909,6 @@ static void alps_get_finger_coordinate_v7(struct input_mt_pos *mt,
unsigned char *pkt,
unsigned char pkt_id)
{
/*
* packet-fmt b7 b6 b5 b4 b3 b2 b1 b0
* Byte0 TWO & MULTI L 1 R M 1 Y0-2 Y0-1 Y0-0
* Byte0 NEW L 1 X1-5 1 1 Y0-2 Y0-1 Y0-0
* Byte1 Y0-10 Y0-9 Y0-8 Y0-7 Y0-6 Y0-5 Y0-4 Y0-3
* Byte2 X0-11 1 X0-10 X0-9 X0-8 X0-7 X0-6 X0-5
* Byte3 X1-11 1 X0-4 X0-3 1 X0-2 X0-1 X0-0
* Byte4 TWO X1-10 TWO X1-9 X1-8 X1-7 X1-6 X1-5 X1-4
* Byte4 MULTI X1-10 TWO X1-9 X1-8 X1-7 X1-6 Y1-5 1
* Byte4 NEW X1-10 TWO X1-9 X1-8 X1-7 X1-6 0 0
* Byte5 TWO & NEW Y1-10 0 Y1-9 Y1-8 Y1-7 Y1-6 Y1-5 Y1-4
* Byte5 MULTI Y1-10 0 Y1-9 Y1-8 Y1-7 Y1-6 F-1 F-0
* L: Left button
* R / M: Non-clickpads: Right / Middle button
* Clickpads: When > 2 fingers are down, and some fingers
* are in the button area, then the 2 coordinates reported
* are for fingers outside the button area and these report
* extra fingers being present in the right / left button
* area. Note these fingers are not added to the F field!
* so if a TWO packet is received and R = 1 then there are
* 3 fingers down, etc.
* TWO: 1: Two touches present, byte 0/4/5 are in TWO fmt
* 0: If byte 4 bit 0 is 1, then byte 0/4/5 are in MULTI fmt
* otherwise byte 0 bit 4 must be set and byte 0/4/5 are
* in NEW fmt
* F: Number of fingers - 3, 0 means 3 fingers, 1 means 4 ...
*/
mt[0].x = ((pkt[2] & 0x80) << 4);
mt[0].x |= ((pkt[2] & 0x3F) << 5);
mt[0].x |= ((pkt[3] & 0x30) >> 1);
@ -1026,16 +1026,12 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
struct input_dev *dev2 = priv->dev2;
int x, y, z, left, right, middle;
/*
* b7 b6 b5 b4 b3 b2 b1 b0
* Byte0 0 1 0 0 1 0 0 0
* Byte1 1 1 * * 1 M R L
* Byte2 X7 1 X5 X4 X3 X2 X1 X0
* Byte3 Z6 1 Y6 X6 1 Y2 Y1 Y0
* Byte4 Y7 0 Y5 Y4 Y3 1 1 0
* Byte5 T&P 0 Z5 Z4 Z3 Z2 Z1 Z0
* M / R / L: Middle / Right / Left button
*/
/* It should be a DualPoint when received trackstick packet */
if (!(priv->flags & ALPS_DUALPOINT)) {
psmouse_warn(psmouse,
"Rejected trackstick packet from non DualPoint device");
return;
}
x = ((packet[2] & 0xbf)) | ((packet[3] & 0x10) << 2);
y = (packet[3] & 0x07) | (packet[4] & 0xb8) |
@ -1089,23 +1085,89 @@ static void alps_process_packet_v7(struct psmouse *psmouse)
alps_process_touchpad_packet_v7(psmouse);
}
static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
static DEFINE_MUTEX(alps_mutex);
static void alps_register_bare_ps2_mouse(struct work_struct *work)
{
struct alps_data *priv =
container_of(work, struct alps_data, dev3_register_work.work);
struct psmouse *psmouse = priv->psmouse;
struct input_dev *dev3;
int error = 0;
mutex_lock(&alps_mutex);
if (priv->dev3)
goto out;
dev3 = input_allocate_device();
if (!dev3) {
psmouse_err(psmouse, "failed to allocate secondary device\n");
error = -ENOMEM;
goto out;
}
snprintf(priv->phys3, sizeof(priv->phys3), "%s/%s",
psmouse->ps2dev.serio->phys,
(priv->dev2 ? "input2" : "input1"));
dev3->phys = priv->phys3;
/*
* format of input device name is: "protocol vendor name"
* see function psmouse_switch_protocol() in psmouse-base.c
*/
dev3->name = "PS/2 ALPS Mouse";
dev3->id.bustype = BUS_I8042;
dev3->id.vendor = 0x0002;
dev3->id.product = PSMOUSE_PS2;
dev3->id.version = 0x0000;
dev3->dev.parent = &psmouse->ps2dev.serio->dev;
input_set_capability(dev3, EV_REL, REL_X);
input_set_capability(dev3, EV_REL, REL_Y);
input_set_capability(dev3, EV_KEY, BTN_LEFT);
input_set_capability(dev3, EV_KEY, BTN_RIGHT);
input_set_capability(dev3, EV_KEY, BTN_MIDDLE);
__set_bit(INPUT_PROP_POINTER, dev3->propbit);
error = input_register_device(dev3);
if (error) {
psmouse_err(psmouse,
"failed to register secondary device: %d\n",
error);
input_free_device(dev3);
goto out;
}
priv->dev3 = dev3;
out:
/*
* Save the error code so that we can detect that we
* already tried to create the device.
*/
if (error)
priv->dev3 = ERR_PTR(error);
mutex_unlock(&alps_mutex);
}
static void alps_report_bare_ps2_packet(struct input_dev *dev,
unsigned char packet[],
bool report_buttons)
{
struct alps_data *priv = psmouse->private;
struct input_dev *dev2 = priv->dev2;
if (report_buttons)
alps_report_buttons(psmouse, dev2, psmouse->dev,
alps_report_buttons(dev, NULL,
packet[0] & 1, packet[0] & 2, packet[0] & 4);
input_report_rel(dev2, REL_X,
input_report_rel(dev, REL_X,
packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0);
input_report_rel(dev2, REL_Y,
input_report_rel(dev, REL_Y,
packet[2] ? ((packet[0] << 3) & 0x100) - packet[2] : 0);
input_sync(dev2);
input_sync(dev);
}
static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse)
@ -1170,8 +1232,8 @@ static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse)
* de-synchronization.
*/
alps_report_bare_ps2_packet(psmouse, &psmouse->packet[3],
false);
alps_report_bare_ps2_packet(priv->dev2,
&psmouse->packet[3], false);
/*
* Continue with the standard ALPS protocol handling,
@ -1227,9 +1289,18 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
* properly we only do this if the device is fully synchronized.
*/
if (!psmouse->out_of_sync_cnt && (psmouse->packet[0] & 0xc8) == 0x08) {
/* Register dev3 mouse if we received PS/2 packet first time */
if (unlikely(!priv->dev3))
psmouse_queue_work(psmouse,
&priv->dev3_register_work, 0);
if (psmouse->pktcnt == 3) {
alps_report_bare_ps2_packet(psmouse, psmouse->packet,
true);
/* Once dev3 mouse device is registered report data */
if (likely(!IS_ERR_OR_NULL(priv->dev3)))
alps_report_bare_ps2_packet(priv->dev3,
psmouse->packet,
true);
return PSMOUSE_FULL_PACKET;
}
return PSMOUSE_GOOD_DATA;
@ -1257,7 +1328,7 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
psmouse->pktcnt - 1,
psmouse->packet[psmouse->pktcnt - 1]);
if (priv->proto_version == ALPS_PROTO_V3 &&
if (priv->proto_version == ALPS_PROTO_V3_RUSHMORE &&
psmouse->pktcnt == psmouse->pktsize) {
/*
* Some Dell boxes, such as Latitude E6440 or E7440
@ -1762,7 +1833,7 @@ static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base)
* all.
*/
if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_SETSCALE21, param)) {
psmouse_warn(psmouse, "trackstick E7 report failed\n");
psmouse_warn(psmouse, "Failed to initialize trackstick (E7 report failed)\n");
ret = -ENODEV;
} else {
psmouse_dbg(psmouse, "trackstick E7 report: %3ph\n", param);
@ -1927,8 +1998,6 @@ static int alps_hw_init_rushmore_v3(struct psmouse *psmouse)
ALPS_REG_BASE_RUSHMORE);
if (reg_val == -EIO)
goto error;
if (reg_val == -ENODEV)
priv->flags &= ~ALPS_DUALPOINT;
}
if (alps_enter_command_mode(psmouse) ||
@ -2144,11 +2213,18 @@ error:
return ret;
}
static void alps_set_defaults(struct alps_data *priv)
static int alps_set_protocol(struct psmouse *psmouse,
struct alps_data *priv,
const struct alps_protocol_info *protocol)
{
priv->byte0 = 0x8f;
priv->mask0 = 0x8f;
priv->flags = ALPS_DUALPOINT;
psmouse->private = priv;
setup_timer(&priv->timer, alps_flush_packet, (unsigned long)psmouse);
priv->proto_version = protocol->version;
priv->byte0 = protocol->byte0;
priv->mask0 = protocol->mask0;
priv->flags = protocol->flags;
priv->x_max = 2000;
priv->y_max = 1400;
@ -2164,6 +2240,7 @@ static void alps_set_defaults(struct alps_data *priv)
priv->x_max = 1023;
priv->y_max = 767;
break;
case ALPS_PROTO_V3:
priv->hw_init = alps_hw_init_v3;
priv->process_packet = alps_process_packet_v3;
@ -2172,6 +2249,23 @@ static void alps_set_defaults(struct alps_data *priv)
priv->nibble_commands = alps_v3_nibble_commands;
priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
break;
case ALPS_PROTO_V3_RUSHMORE:
priv->hw_init = alps_hw_init_rushmore_v3;
priv->process_packet = alps_process_packet_v3;
priv->set_abs_params = alps_set_abs_params_mt;
priv->decode_fields = alps_decode_rushmore;
priv->nibble_commands = alps_v3_nibble_commands;
priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
priv->x_bits = 16;
priv->y_bits = 12;
if (alps_probe_trackstick_v3(psmouse,
ALPS_REG_BASE_RUSHMORE) < 0)
priv->flags &= ~ALPS_DUALPOINT;
break;
case ALPS_PROTO_V4:
priv->hw_init = alps_hw_init_v4;
priv->process_packet = alps_process_packet_v4;
@ -2179,6 +2273,7 @@ static void alps_set_defaults(struct alps_data *priv)
priv->nibble_commands = alps_v4_nibble_commands;
priv->addr_command = PSMOUSE_CMD_DISABLE;
break;
case ALPS_PROTO_V5:
priv->hw_init = alps_hw_init_dolphin_v1;
priv->process_packet = alps_process_touchpad_packet_v3_v5;
@ -2186,14 +2281,12 @@ static void alps_set_defaults(struct alps_data *priv)
priv->set_abs_params = alps_set_abs_params_mt;
priv->nibble_commands = alps_v3_nibble_commands;
priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
priv->byte0 = 0xc8;
priv->mask0 = 0xd8;
priv->flags = 0;
priv->x_max = 1360;
priv->y_max = 660;
priv->x_bits = 23;
priv->y_bits = 12;
break;
case ALPS_PROTO_V6:
priv->hw_init = alps_hw_init_v6;
priv->process_packet = alps_process_packet_v6;
@ -2202,6 +2295,7 @@ static void alps_set_defaults(struct alps_data *priv)
priv->x_max = 2047;
priv->y_max = 1535;
break;
case ALPS_PROTO_V7:
priv->hw_init = alps_hw_init_v7;
priv->process_packet = alps_process_packet_v7;
@ -2209,19 +2303,21 @@ static void alps_set_defaults(struct alps_data *priv)
priv->set_abs_params = alps_set_abs_params_mt;
priv->nibble_commands = alps_v3_nibble_commands;
priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
priv->x_max = 0xfff;
priv->y_max = 0x7ff;
priv->byte0 = 0x48;
priv->mask0 = 0x48;
if (alps_dolphin_get_device_area(psmouse, priv))
return -EIO;
if (priv->fw_ver[1] != 0xba)
priv->flags |= ALPS_BUTTONPAD;
break;
}
return 0;
}
static int alps_match_table(struct psmouse *psmouse, struct alps_data *priv,
unsigned char *e7, unsigned char *ec)
static const struct alps_protocol_info *alps_match_table(unsigned char *e7,
unsigned char *ec)
{
const struct alps_model_info *model;
int i;
@ -2233,23 +2329,18 @@ static int alps_match_table(struct psmouse *psmouse, struct alps_data *priv,
(!model->command_mode_resp ||
model->command_mode_resp == ec[2])) {
priv->proto_version = model->proto_version;
alps_set_defaults(priv);
priv->flags = model->flags;
priv->byte0 = model->byte0;
priv->mask0 = model->mask0;
return 0;
return &model->protocol_info;
}
}
return -EINVAL;
return NULL;
}
static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
{
const struct alps_protocol_info *protocol;
unsigned char e6[4], e7[4], ec[4];
int error;
/*
* First try "E6 report".
@ -2275,54 +2366,35 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
alps_exit_command_mode(psmouse))
return -EIO;
/* Save the Firmware version */
memcpy(priv->fw_ver, ec, 3);
if (alps_match_table(psmouse, priv, e7, ec) == 0) {
return 0;
} else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 &&
ec[0] == 0x73 && (ec[1] == 0x01 || ec[1] == 0x02)) {
priv->proto_version = ALPS_PROTO_V5;
alps_set_defaults(priv);
if (alps_dolphin_get_device_area(psmouse, priv))
return -EIO;
else
return 0;
} else if (ec[0] == 0x88 &&
((ec[1] & 0xf0) == 0xb0 || (ec[1] & 0xf0) == 0xc0)) {
priv->proto_version = ALPS_PROTO_V7;
alps_set_defaults(priv);
return 0;
} else if (ec[0] == 0x88 && ec[1] == 0x08) {
priv->proto_version = ALPS_PROTO_V3;
alps_set_defaults(priv);
priv->hw_init = alps_hw_init_rushmore_v3;
priv->decode_fields = alps_decode_rushmore;
priv->x_bits = 16;
priv->y_bits = 12;
priv->flags |= ALPS_IS_RUSHMORE;
/* hack to make addr_command, nibble_command available */
psmouse->private = priv;
if (alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_RUSHMORE))
priv->flags &= ~ALPS_DUALPOINT;
return 0;
} else if (ec[0] == 0x88 && ec[1] == 0x07 &&
ec[2] >= 0x90 && ec[2] <= 0x9d) {
priv->proto_version = ALPS_PROTO_V3;
alps_set_defaults(priv);
return 0;
protocol = alps_match_table(e7, ec);
if (!protocol) {
if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 &&
ec[0] == 0x73 && (ec[1] == 0x01 || ec[1] == 0x02)) {
protocol = &alps_v5_protocol_data;
} else if (ec[0] == 0x88 &&
((ec[1] & 0xf0) == 0xb0 || (ec[1] & 0xf0) == 0xc0)) {
protocol = &alps_v7_protocol_data;
} else if (ec[0] == 0x88 && ec[1] == 0x08) {
protocol = &alps_v3_rushmore_data;
} else if (ec[0] == 0x88 && ec[1] == 0x07 &&
ec[2] >= 0x90 && ec[2] <= 0x9d) {
protocol = &alps_v3_protocol_data;
} else {
psmouse_dbg(psmouse,
"Likely not an ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec);
return -EINVAL;
}
}
psmouse_dbg(psmouse,
"Likely not an ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec);
if (priv) {
/* Save the Firmware version */
memcpy(priv->fw_ver, ec, 3);
error = alps_set_protocol(psmouse, priv, protocol);
if (error)
return error;
}
return -EINVAL;
return 0;
}
static int alps_reconnect(struct psmouse *psmouse)
@ -2343,7 +2415,10 @@ static void alps_disconnect(struct psmouse *psmouse)
psmouse_reset(psmouse);
del_timer_sync(&priv->timer);
input_unregister_device(priv->dev2);
if (priv->dev2)
input_unregister_device(priv->dev2);
if (!IS_ERR_OR_NULL(priv->dev3))
input_unregister_device(priv->dev3);
kfree(priv);
}
@ -2376,25 +2451,12 @@ static void alps_set_abs_params_mt(struct alps_data *priv,
int alps_init(struct psmouse *psmouse)
{
struct alps_data *priv;
struct input_dev *dev1 = psmouse->dev, *dev2;
struct alps_data *priv = psmouse->private;
struct input_dev *dev1 = psmouse->dev;
int error;
priv = kzalloc(sizeof(struct alps_data), GFP_KERNEL);
dev2 = input_allocate_device();
if (!priv || !dev2)
goto init_fail;
priv->dev2 = dev2;
setup_timer(&priv->timer, alps_flush_packet, (unsigned long)psmouse);
psmouse->private = priv;
psmouse_reset(psmouse);
if (alps_identify(psmouse, priv) < 0)
goto init_fail;
if (priv->hw_init(psmouse))
error = priv->hw_init(psmouse);
if (error)
goto init_fail;
/*
@ -2443,27 +2505,58 @@ int alps_init(struct psmouse *psmouse)
dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE);
}
snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys);
dev2->phys = priv->phys;
dev2->name = (priv->flags & ALPS_DUALPOINT) ?
"DualPoint Stick" : "ALPS PS/2 Device";
dev2->id.bustype = BUS_I8042;
dev2->id.vendor = 0x0002;
dev2->id.product = PSMOUSE_ALPS;
dev2->id.version = 0x0000;
dev2->dev.parent = &psmouse->ps2dev.serio->dev;
if (priv->flags & ALPS_DUALPOINT) {
struct input_dev *dev2;
dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
dev2->keybit[BIT_WORD(BTN_LEFT)] =
BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
dev2 = input_allocate_device();
if (!dev2) {
psmouse_err(psmouse,
"failed to allocate trackstick device\n");
error = -ENOMEM;
goto init_fail;
}
__set_bit(INPUT_PROP_POINTER, dev2->propbit);
if (priv->flags & ALPS_DUALPOINT)
snprintf(priv->phys2, sizeof(priv->phys2), "%s/input1",
psmouse->ps2dev.serio->phys);
dev2->phys = priv->phys2;
/*
* format of input device name is: "protocol vendor name"
* see function psmouse_switch_protocol() in psmouse-base.c
*/
dev2->name = "AlpsPS/2 ALPS DualPoint Stick";
dev2->id.bustype = BUS_I8042;
dev2->id.vendor = 0x0002;
dev2->id.product = PSMOUSE_ALPS;
dev2->id.version = priv->proto_version;
dev2->dev.parent = &psmouse->ps2dev.serio->dev;
input_set_capability(dev2, EV_REL, REL_X);
input_set_capability(dev2, EV_REL, REL_Y);
input_set_capability(dev2, EV_KEY, BTN_LEFT);
input_set_capability(dev2, EV_KEY, BTN_RIGHT);
input_set_capability(dev2, EV_KEY, BTN_MIDDLE);
__set_bit(INPUT_PROP_POINTER, dev2->propbit);
__set_bit(INPUT_PROP_POINTING_STICK, dev2->propbit);
if (input_register_device(priv->dev2))
goto init_fail;
error = input_register_device(dev2);
if (error) {
psmouse_err(psmouse,
"failed to register trackstick device: %d\n",
error);
input_free_device(dev2);
goto init_fail;
}
priv->dev2 = dev2;
}
priv->psmouse = psmouse;
INIT_DELAYED_WORK(&priv->dev3_register_work,
alps_register_bare_ps2_mouse);
psmouse->protocol_handler = alps_process_byte;
psmouse->poll = alps_poll;
@ -2481,25 +2574,56 @@ int alps_init(struct psmouse *psmouse)
init_fail:
psmouse_reset(psmouse);
input_free_device(dev2);
kfree(priv);
/*
* Even though we did not allocate psmouse->private we do free
* it here.
*/
kfree(psmouse->private);
psmouse->private = NULL;
return -1;
return error;
}
int alps_detect(struct psmouse *psmouse, bool set_properties)
{
struct alps_data dummy;
struct alps_data *priv;
int error;
if (alps_identify(psmouse, &dummy) < 0)
return -1;
error = alps_identify(psmouse, NULL);
if (error)
return error;
/*
* Reset the device to make sure it is fully operational:
* on some laptops, like certain Dell Latitudes, we may
* fail to properly detect presence of trackstick if device
* has not been reset.
*/
psmouse_reset(psmouse);
priv = kzalloc(sizeof(struct alps_data), GFP_KERNEL);
if (!priv)
return -ENOMEM;
error = alps_identify(psmouse, priv);
if (error)
return error;
if (set_properties) {
psmouse->vendor = "ALPS";
psmouse->name = dummy.flags & ALPS_DUALPOINT ?
psmouse->name = priv->flags & ALPS_DUALPOINT ?
"DualPoint TouchPad" : "GlidePoint";
psmouse->model = dummy.proto_version << 8;
psmouse->model = priv->proto_version;
} else {
/*
* Destroy alps_data structure we allocated earlier since
* this was just a "trial run". Otherwise we'll keep it
* to be used by alps_init() which has to be called if
* we succeed and set_properties is true.
*/
kfree(priv);
psmouse->private = NULL;
}
return 0;
}

View file

@ -14,13 +14,14 @@
#include <linux/input/mt.h>
#define ALPS_PROTO_V1 1
#define ALPS_PROTO_V2 2
#define ALPS_PROTO_V3 3
#define ALPS_PROTO_V4 4
#define ALPS_PROTO_V5 5
#define ALPS_PROTO_V6 6
#define ALPS_PROTO_V7 7 /* t3btl t4s */
#define ALPS_PROTO_V1 0x100
#define ALPS_PROTO_V2 0x200
#define ALPS_PROTO_V3 0x300
#define ALPS_PROTO_V3_RUSHMORE 0x310
#define ALPS_PROTO_V4 0x400
#define ALPS_PROTO_V5 0x500
#define ALPS_PROTO_V6 0x600
#define ALPS_PROTO_V7 0x700 /* t3btl t4s */
#define MAX_TOUCHES 2
@ -45,6 +46,21 @@ enum V7_PACKET_ID {
V7_PACKET_ID_UNKNOWN,
};
/**
* struct alps_protocol_info - information about protocol used by a device
* @version: Indicates V1/V2/V3/...
* @byte0: Helps figure out whether a position report packet matches the
* known format for this model. The first byte of the report, ANDed with
* mask0, should match byte0.
* @mask0: The mask used to check the first byte of the report.
* @flags: Additional device capabilities (passthrough port, trackstick, etc.).
*/
struct alps_protocol_info {
u16 version;
u8 byte0, mask0;
unsigned int flags;
};
/**
* struct alps_model_info - touchpad ID table
* @signature: E7 response string to match.
@ -52,23 +68,16 @@ enum V7_PACKET_ID {
* (aka command mode response) identifies the firmware minor version. This
* can be used to distinguish different hardware models which are not
* uniquely identifiable through their E7 responses.
* @proto_version: Indicates V1/V2/V3/...
* @byte0: Helps figure out whether a position report packet matches the
* known format for this model. The first byte of the report, ANDed with
* mask0, should match byte0.
* @mask0: The mask used to check the first byte of the report.
* @flags: Additional device capabilities (passthrough port, trackstick, etc.).
* @protocol_info: information about protcol used by the device.
*
* Many (but not all) ALPS touchpads can be identified by looking at the
* values returned in the "E7 report" and/or the "EC report." This table
* lists a number of such touchpads.
*/
struct alps_model_info {
unsigned char signature[3];
unsigned char command_mode_resp;
unsigned char proto_version;
unsigned char byte0, mask0;
int flags;
u8 signature[3];
u8 command_mode_resp;
struct alps_protocol_info protocol_info;
};
/**
@ -132,8 +141,12 @@ struct alps_fields {
/**
* struct alps_data - private data structure for the ALPS driver
* @dev2: "Relative" device used to report trackstick or mouse activity.
* @phys: Physical path for the relative device.
* @psmouse: Pointer to parent psmouse device
* @dev2: Trackstick device (can be NULL).
* @dev3: Generic PS/2 mouse (can be NULL, delayed registering).
* @phys2: Physical path for the trackstick device.
* @phys3: Physical path for the generic PS/2 mouse.
* @dev3_register_work: Delayed work for registering PS/2 mouse.
* @nibble_commands: Command mapping used for touchpad register accesses.
* @addr_command: Command used to tell the touchpad that a register address
* follows.
@ -160,15 +173,19 @@ struct alps_fields {
* @timer: Timer for flushing out the final report packet in the stream.
*/
struct alps_data {
struct psmouse *psmouse;
struct input_dev *dev2;
char phys[32];
struct input_dev *dev3;
char phys2[32];
char phys3[32];
struct delayed_work dev3_register_work;
/* these are autodetected when the device is identified */
const struct alps_nibble_commands *nibble_commands;
int addr_command;
unsigned char proto_version;
unsigned char byte0, mask0;
unsigned char fw_ver[3];
u16 proto_version;
u8 byte0, mask0;
u8 fw_ver[3];
int flags;
int x_max;
int y_max;

View file

@ -564,7 +564,7 @@ static int report_tp_state(struct bcm5974 *dev, int size)
dev->index[n++] = &f[i];
}
input_mt_assign_slots(input, dev->slots, dev->pos, n);
input_mt_assign_slots(input, dev->slots, dev->pos, n, 0);
for (i = 0; i < n; i++)
report_finger_data(input, dev->slots[i],

File diff suppressed because it is too large Load diff

301
drivers/input/mouse/cyapa.h Normal file
View file

@ -0,0 +1,301 @@
/*
* Cypress APA trackpad with I2C interface
*
* Author: Dudley Du <dudl@cypress.com>
*
* Copyright (C) 2014 Cypress Semiconductor, Inc.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
#ifndef _CYAPA_H
#define _CYAPA_H
#include <linux/firmware.h>
/* APA trackpad firmware generation number. */
#define CYAPA_GEN_UNKNOWN 0x00 /* unknown protocol. */
#define CYAPA_GEN3 0x03 /* support MT-protocol B with tracking ID. */
#define CYAPA_GEN5 0x05 /* support TrueTouch GEN5 trackpad device. */
#define CYAPA_NAME "Cypress APA Trackpad (cyapa)"
/*
* Macros for SMBus communication
*/
#define SMBUS_READ 0x01
#define SMBUS_WRITE 0x00
#define SMBUS_ENCODE_IDX(cmd, idx) ((cmd) | (((idx) & 0x03) << 1))
#define SMBUS_ENCODE_RW(cmd, rw) ((cmd) | ((rw) & 0x01))
#define SMBUS_BYTE_BLOCK_CMD_MASK 0x80
#define SMBUS_GROUP_BLOCK_CMD_MASK 0x40
/* Commands for read/write registers of Cypress trackpad */
#define CYAPA_CMD_SOFT_RESET 0x00
#define CYAPA_CMD_POWER_MODE 0x01
#define CYAPA_CMD_DEV_STATUS 0x02
#define CYAPA_CMD_GROUP_DATA 0x03
#define CYAPA_CMD_GROUP_CMD 0x04
#define CYAPA_CMD_GROUP_QUERY 0x05
#define CYAPA_CMD_BL_STATUS 0x06
#define CYAPA_CMD_BL_HEAD 0x07
#define CYAPA_CMD_BL_CMD 0x08
#define CYAPA_CMD_BL_DATA 0x09
#define CYAPA_CMD_BL_ALL 0x0a
#define CYAPA_CMD_BLK_PRODUCT_ID 0x0b
#define CYAPA_CMD_BLK_HEAD 0x0c
#define CYAPA_CMD_MAX_BASELINE 0x0d
#define CYAPA_CMD_MIN_BASELINE 0x0e
#define BL_HEAD_OFFSET 0x00
#define BL_DATA_OFFSET 0x10
#define BL_STATUS_SIZE 3 /* Length of gen3 bootloader status registers */
#define CYAPA_REG_MAP_SIZE 256
/*
* Gen3 Operational Device Status Register
*
* bit 7: Valid interrupt source
* bit 6 - 4: Reserved
* bit 3 - 2: Power status
* bit 1 - 0: Device status
*/
#define REG_OP_STATUS 0x00
#define OP_STATUS_SRC 0x80
#define OP_STATUS_POWER 0x0c
#define OP_STATUS_DEV 0x03
#define OP_STATUS_MASK (OP_STATUS_SRC | OP_STATUS_POWER | OP_STATUS_DEV)
/*
* Operational Finger Count/Button Flags Register
*
* bit 7 - 4: Number of touched finger
* bit 3: Valid data
* bit 2: Middle Physical Button
* bit 1: Right Physical Button
* bit 0: Left physical Button
*/
#define REG_OP_DATA1 0x01
#define OP_DATA_VALID 0x08
#define OP_DATA_MIDDLE_BTN 0x04
#define OP_DATA_RIGHT_BTN 0x02
#define OP_DATA_LEFT_BTN 0x01
#define OP_DATA_BTN_MASK (OP_DATA_MIDDLE_BTN | OP_DATA_RIGHT_BTN | \
OP_DATA_LEFT_BTN)
/*
* Write-only command file register used to issue commands and
* parameters to the bootloader.
* The default value read from it is always 0x00.
*/
#define REG_BL_FILE 0x00
#define BL_FILE 0x00
/*
* Bootloader Status Register
*
* bit 7: Busy
* bit 6 - 5: Reserved
* bit 4: Bootloader running
* bit 3 - 2: Reserved
* bit 1: Watchdog Reset
* bit 0: Checksum valid
*/
#define REG_BL_STATUS 0x01
#define BL_STATUS_REV_6_5 0x60
#define BL_STATUS_BUSY 0x80
#define BL_STATUS_RUNNING 0x10
#define BL_STATUS_REV_3_2 0x0c
#define BL_STATUS_WATCHDOG 0x02
#define BL_STATUS_CSUM_VALID 0x01
#define BL_STATUS_REV_MASK (BL_STATUS_WATCHDOG | BL_STATUS_REV_3_2 | \
BL_STATUS_REV_6_5)
/*
* Bootloader Error Register
*
* bit 7: Invalid
* bit 6: Invalid security key
* bit 5: Bootloading
* bit 4: Command checksum
* bit 3: Flash protection error
* bit 2: Flash checksum error
* bit 1 - 0: Reserved
*/
#define REG_BL_ERROR 0x02
#define BL_ERROR_INVALID 0x80
#define BL_ERROR_INVALID_KEY 0x40
#define BL_ERROR_BOOTLOADING 0x20
#define BL_ERROR_CMD_CSUM 0x10
#define BL_ERROR_FLASH_PROT 0x08
#define BL_ERROR_FLASH_CSUM 0x04
#define BL_ERROR_RESERVED 0x03
#define BL_ERROR_NO_ERR_IDLE 0x00
#define BL_ERROR_NO_ERR_ACTIVE (BL_ERROR_BOOTLOADING)
#define CAPABILITY_BTN_SHIFT 3
#define CAPABILITY_LEFT_BTN_MASK (0x01 << 3)
#define CAPABILITY_RIGHT_BTN_MASK (0x01 << 4)
#define CAPABILITY_MIDDLE_BTN_MASK (0x01 << 5)
#define CAPABILITY_BTN_MASK (CAPABILITY_LEFT_BTN_MASK | \
CAPABILITY_RIGHT_BTN_MASK | \
CAPABILITY_MIDDLE_BTN_MASK)
#define PWR_MODE_MASK 0xfc
#define PWR_MODE_FULL_ACTIVE (0x3f << 2)
#define PWR_MODE_IDLE (0x03 << 2) /* Default rt suspend scanrate: 30ms */
#define PWR_MODE_SLEEP (0x05 << 2) /* Default suspend scanrate: 50ms */
#define PWR_MODE_BTN_ONLY (0x01 << 2)
#define PWR_MODE_OFF (0x00 << 2)
#define PWR_STATUS_MASK 0x0c
#define PWR_STATUS_ACTIVE (0x03 << 2)
#define PWR_STATUS_IDLE (0x02 << 2)
#define PWR_STATUS_BTN_ONLY (0x01 << 2)
#define PWR_STATUS_OFF (0x00 << 2)
#define AUTOSUSPEND_DELAY 2000 /* unit : ms */
#define UNINIT_SLEEP_TIME 0xFFFF
#define UNINIT_PWR_MODE 0xFF
#define BTN_ONLY_MODE_NAME "buttononly"
#define OFF_MODE_NAME "off"
/* The touch.id is used as the MT slot id, thus max MT slot is 15 */
#define CYAPA_MAX_MT_SLOTS 15
struct cyapa;
typedef bool (*cb_sort)(struct cyapa *, u8 *, int);
struct cyapa_dev_ops {
int (*check_fw)(struct cyapa *, const struct firmware *);
int (*bl_enter)(struct cyapa *);
int (*bl_activate)(struct cyapa *);
int (*bl_initiate)(struct cyapa *, const struct firmware *);
int (*update_fw)(struct cyapa *, const struct firmware *);
int (*bl_deactivate)(struct cyapa *);
ssize_t (*show_baseline)(struct device *,
struct device_attribute *, char *);
ssize_t (*calibrate_store)(struct device *,
struct device_attribute *, const char *, size_t);
int (*initialize)(struct cyapa *cyapa);
int (*state_parse)(struct cyapa *cyapa, u8 *reg_status, int len);
int (*operational_check)(struct cyapa *cyapa);
int (*irq_handler)(struct cyapa *);
bool (*irq_cmd_handler)(struct cyapa *);
int (*sort_empty_output_data)(struct cyapa *,
u8 *, int *, cb_sort);
int (*set_power_mode)(struct cyapa *, u8, u16);
};
struct cyapa_gen5_cmd_states {
struct mutex cmd_lock;
struct completion cmd_ready;
atomic_t cmd_issued;
u8 in_progress_cmd;
bool is_irq_mode;
cb_sort resp_sort_func;
u8 *resp_data;
int *resp_len;
u8 irq_cmd_buf[CYAPA_REG_MAP_SIZE];
u8 empty_buf[CYAPA_REG_MAP_SIZE];
};
union cyapa_cmd_states {
struct cyapa_gen5_cmd_states gen5;
};
enum cyapa_state {
CYAPA_STATE_NO_DEVICE,
CYAPA_STATE_BL_BUSY,
CYAPA_STATE_BL_IDLE,
CYAPA_STATE_BL_ACTIVE,
CYAPA_STATE_OP,
CYAPA_STATE_GEN5_BL,
CYAPA_STATE_GEN5_APP,
};
/* The main device structure */
struct cyapa {
enum cyapa_state state;
u8 status[BL_STATUS_SIZE];
bool operational; /* true: ready for data reporting; false: not. */
struct i2c_client *client;
struct input_dev *input;
char phys[32]; /* Device physical location */
bool irq_wake; /* Irq wake is enabled */
bool smbus;
/* power mode settings */
u8 suspend_power_mode;
u16 suspend_sleep_time;
u8 runtime_suspend_power_mode;
u16 runtime_suspend_sleep_time;
u8 dev_pwr_mode;
u16 dev_sleep_time;
/* Read from query data region. */
char product_id[16];
u8 fw_maj_ver; /* Firmware major version. */
u8 fw_min_ver; /* Firmware minor version. */
u8 btn_capability;
u8 gen;
int max_abs_x;
int max_abs_y;
int physical_size_x;
int physical_size_y;
/* Used in ttsp and truetouch based trackpad devices. */
u8 x_origin; /* X Axis Origin: 0 = left side; 1 = rigth side. */
u8 y_origin; /* Y Axis Origin: 0 = top; 1 = bottom. */
int electrodes_x; /* Number of electrodes on the X Axis*/
int electrodes_y; /* Number of electrodes on the Y Axis*/
int electrodes_rx; /* Number of Rx electrodes */
int aligned_electrodes_rx; /* 4 aligned */
int max_z;
/*
* Used to synchronize the access or update the device state.
* And since update firmware and read firmware image process will take
* quite long time, maybe more than 10 seconds, so use mutex_lock
* to sync and wait other interface and detecting are done or ready.
*/
struct mutex state_sync_lock;
const struct cyapa_dev_ops *ops;
union cyapa_cmd_states cmd_states;
};
ssize_t cyapa_i2c_reg_read_block(struct cyapa *cyapa, u8 reg, size_t len,
u8 *values);
ssize_t cyapa_smbus_read_block(struct cyapa *cyapa, u8 cmd, size_t len,
u8 *values);
ssize_t cyapa_read_block(struct cyapa *cyapa, u8 cmd_idx, u8 *values);
int cyapa_poll_state(struct cyapa *cyapa, unsigned int timeout);
u8 cyapa_sleep_time_to_pwr_cmd(u16 sleep_time);
u16 cyapa_pwr_cmd_to_sleep_time(u8 pwr_mode);
extern const char product_id[];
extern const struct cyapa_dev_ops cyapa_gen3_ops;
extern const struct cyapa_dev_ops cyapa_gen5_ops;
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -538,7 +538,7 @@ static void cypress_process_packet(struct psmouse *psmouse, bool zero_pkt)
pos[i].y = contact->y;
}
input_mt_assign_slots(input, slots, pos, n);
input_mt_assign_slots(input, slots, pos, n, 0);
for (i = 0; i < n; i++) {
contact = &report_data.contacts[i];
@ -710,8 +710,3 @@ err_exit:
return -1;
}
bool cypress_supported(void)
{
return true;
}

View file

@ -172,7 +172,6 @@ struct cytp_data {
#ifdef CONFIG_MOUSE_PS2_CYPRESS
int cypress_detect(struct psmouse *psmouse, bool set_properties);
int cypress_init(struct psmouse *psmouse);
bool cypress_supported(void);
#else
inline int cypress_detect(struct psmouse *psmouse, bool set_properties)
{
@ -182,10 +181,6 @@ inline int cypress_init(struct psmouse *psmouse)
{
return -ENOSYS;
}
inline bool cypress_supported(void)
{
return 0;
}
#endif /* CONFIG_MOUSE_PS2_CYPRESS */
#endif /* _CYPRESS_PS2_H */

View file

@ -4,7 +4,6 @@
* Copyright (c) 2013 ELAN Microelectronics Corp.
*
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
* Version: 1.5.5
*
* Based on cyapa driver:
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
@ -33,8 +32,9 @@
#define ETP_FW_IAP_PAGE_ERR (1 << 5)
#define ETP_FW_IAP_INTF_ERR (1 << 4)
#define ETP_FW_PAGE_SIZE 64
#define ETP_FW_PAGE_COUNT 768
#define ETP_FW_SIZE (ETP_FW_PAGE_SIZE * ETP_FW_PAGE_COUNT)
#define ETP_FW_VAILDPAGE_COUNT 768
#define ETP_FW_SIGNATURE_SIZE 6
#define ETP_FW_SIGNATURE_ADDRESS 0xBFFA
struct i2c_client;
struct completion;

View file

@ -4,7 +4,7 @@
* Copyright (c) 2013 ELAN Microelectronics Corp.
*
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
* Version: 1.5.5
* Version: 1.5.6
*
* Based on cyapa driver:
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
@ -40,7 +40,7 @@
#include "elan_i2c.h"
#define DRIVER_NAME "elan_i2c"
#define ELAN_DRIVER_VERSION "1.5.5"
#define ELAN_DRIVER_VERSION "1.5.6"
#define ETP_PRESSURE_OFFSET 25
#define ETP_MAX_PRESSURE 255
#define ETP_FWIDTH_REDUCE 90
@ -312,7 +312,7 @@ static int __elan_update_firmware(struct elan_tp_data *data,
iap_start_addr = get_unaligned_le16(&fw->data[ETP_IAP_START_ADDR * 2]);
boot_page_count = (iap_start_addr * 2) / ETP_FW_PAGE_SIZE;
for (i = boot_page_count; i < ETP_FW_PAGE_COUNT; i++) {
for (i = boot_page_count; i < ETP_FW_VAILDPAGE_COUNT; i++) {
u16 checksum = 0;
const u8 *page = &fw->data[i * ETP_FW_PAGE_SIZE];
@ -434,10 +434,11 @@ static ssize_t elan_sysfs_update_fw(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct elan_tp_data *data = i2c_get_clientdata(client);
struct elan_tp_data *data = dev_get_drvdata(dev);
const struct firmware *fw;
int error;
const u8 *fw_signature;
static const u8 signature[] = {0xAA, 0x55, 0xCC, 0x33, 0xFF, 0xFF};
error = request_firmware(&fw, ETP_FW_NAME, dev);
if (error) {
@ -446,10 +447,12 @@ static ssize_t elan_sysfs_update_fw(struct device *dev,
return error;
}
/* Firmware must be exactly PAGE_NUM * PAGE_SIZE bytes */
if (fw->size != ETP_FW_SIZE) {
dev_err(dev, "invalid firmware size = %zu, expected %d.\n",
fw->size, ETP_FW_SIZE);
/* Firmware file must match signature data */
fw_signature = &fw->data[ETP_FW_SIGNATURE_ADDRESS];
if (memcmp(fw_signature, signature, sizeof(signature)) != 0) {
dev_err(dev, "signature mismatch (expected %*ph, got %*ph)\n",
(int)sizeof(signature), signature,
(int)sizeof(signature), fw_signature);
error = -EBADF;
goto out_release_fw;
}

View file

@ -4,7 +4,6 @@
* Copyright (c) 2013 ELAN Microelectronics Corp.
*
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
* Version: 1.5.5
*
* Based on cyapa driver:
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.

View file

@ -4,7 +4,6 @@
* Copyright (c) 2013 ELAN Microelectronics Corp.
*
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
* Version: 1.5.5
*
* Based on cyapa driver:
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
@ -71,7 +70,7 @@ static int elan_smbus_initialize(struct i2c_client *client)
/* compare hello packet */
if (memcmp(values, check, ETP_SMBUS_HELLOPACKET_LEN)) {
dev_err(&client->dev, "hello packet fail [%*px]\n",
dev_err(&client->dev, "hello packet fail [%*ph]\n",
ETP_SMBUS_HELLOPACKET_LEN, values);
return -ENXIO;
}

View file

@ -2,6 +2,7 @@
* Focaltech TouchPad PS/2 mouse driver
*
* Copyright (c) 2014 Red Hat Inc.
* Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -13,15 +14,14 @@
* Hans de Goede <hdegoede@redhat.com>
*/
/*
* The Focaltech PS/2 touchpad protocol is unknown. This drivers deals with
* detection only, to avoid further detection attempts confusing the touchpad
* this way it at least works in PS/2 mouse compatibility mode.
*/
#include <linux/device.h>
#include <linux/libps2.h>
#include <linux/input/mt.h>
#include <linux/serio.h>
#include <linux/slab.h>
#include "psmouse.h"
#include "focaltech.h"
static const char * const focaltech_pnp_ids[] = {
"FLT0101",
@ -30,6 +30,12 @@ static const char * const focaltech_pnp_ids[] = {
NULL
};
/*
* Even if the kernel is built without support for Focaltech PS/2 touchpads (or
* when the real driver fails to recognize the device), we still have to detect
* them in order to avoid further detection attempts confusing the touchpad.
* This way it at least works in PS/2 mouse compatibility mode.
*/
int focaltech_detect(struct psmouse *psmouse, bool set_properties)
{
if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
@ -37,16 +43,394 @@ int focaltech_detect(struct psmouse *psmouse, bool set_properties)
if (set_properties) {
psmouse->vendor = "FocalTech";
psmouse->name = "FocalTech Touchpad in mouse emulation mode";
psmouse->name = "FocalTech Touchpad";
}
return 0;
}
int focaltech_init(struct psmouse *psmouse)
static void focaltech_reset(struct psmouse *psmouse)
{
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
psmouse_reset(psmouse);
}
#ifdef CONFIG_MOUSE_PS2_FOCALTECH
/*
* Packet types - the numbers are not consecutive, so we might be missing
* something here.
*/
#define FOC_TOUCH 0x3 /* bitmap of active fingers */
#define FOC_ABS 0x6 /* absolute position of one finger */
#define FOC_REL 0x9 /* relative position of 1-2 fingers */
#define FOC_MAX_FINGERS 5
#define FOC_MAX_X 2431
#define FOC_MAX_Y 1663
/*
* Current state of a single finger on the touchpad.
*/
struct focaltech_finger_state {
/* The touchpad has generated a touch event for the finger */
bool active;
/*
* The touchpad has sent position data for the finger. The
* flag is 0 when the finger is not active, and there is a
* time between the first touch event for the finger and the
* following absolute position packet for the finger where the
* touchpad has declared the finger to be valid, but we do not
* have any valid position yet.
*/
bool valid;
/*
* Absolute position (from the bottom left corner) of the
* finger.
*/
unsigned int x;
unsigned int y;
};
/*
* Description of the current state of the touchpad hardware.
*/
struct focaltech_hw_state {
/*
* The touchpad tracks the positions of the fingers for us,
* the array indices correspond to the finger indices returned
* in the report packages.
*/
struct focaltech_finger_state fingers[FOC_MAX_FINGERS];
/* True if the clickpad has been pressed. */
bool pressed;
};
struct focaltech_data {
unsigned int x_max, y_max;
struct focaltech_hw_state state;
};
static void focaltech_report_state(struct psmouse *psmouse)
{
struct focaltech_data *priv = psmouse->private;
struct focaltech_hw_state *state = &priv->state;
struct input_dev *dev = psmouse->dev;
int i;
for (i = 0; i < FOC_MAX_FINGERS; i++) {
struct focaltech_finger_state *finger = &state->fingers[i];
bool active = finger->active && finger->valid;
input_mt_slot(dev, i);
input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
if (active) {
input_report_abs(dev, ABS_MT_POSITION_X, finger->x);
input_report_abs(dev, ABS_MT_POSITION_Y,
FOC_MAX_Y - finger->y);
}
}
input_mt_report_pointer_emulation(dev, true);
input_report_key(psmouse->dev, BTN_LEFT, state->pressed);
input_sync(psmouse->dev);
}
static void focaltech_process_touch_packet(struct psmouse *psmouse,
unsigned char *packet)
{
struct focaltech_data *priv = psmouse->private;
struct focaltech_hw_state *state = &priv->state;
unsigned char fingers = packet[1];
int i;
state->pressed = (packet[0] >> 4) & 1;
/* the second byte contains a bitmap of all fingers touching the pad */
for (i = 0; i < FOC_MAX_FINGERS; i++) {
state->fingers[i].active = fingers & 0x1;
if (!state->fingers[i].active) {
/*
* Even when the finger becomes active again, we still
* will have to wait for the first valid position.
*/
state->fingers[i].valid = false;
}
fingers >>= 1;
}
}
static void focaltech_process_abs_packet(struct psmouse *psmouse,
unsigned char *packet)
{
struct focaltech_data *priv = psmouse->private;
struct focaltech_hw_state *state = &priv->state;
unsigned int finger;
finger = (packet[1] >> 4) - 1;
if (finger >= FOC_MAX_FINGERS) {
psmouse_err(psmouse, "Invalid finger in abs packet: %d\n",
finger);
return;
}
state->pressed = (packet[0] >> 4) & 1;
/*
* packet[5] contains some kind of tool size in the most
* significant nibble. 0xff is a special value (latching) that
* signals a large contact area.
*/
if (packet[5] == 0xff) {
state->fingers[finger].valid = false;
return;
}
state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2];
state->fingers[finger].y = (packet[3] << 8) | packet[4];
state->fingers[finger].valid = true;
}
static void focaltech_process_rel_packet(struct psmouse *psmouse,
unsigned char *packet)
{
struct focaltech_data *priv = psmouse->private;
struct focaltech_hw_state *state = &priv->state;
int finger1, finger2;
state->pressed = packet[0] >> 7;
finger1 = ((packet[0] >> 4) & 0x7) - 1;
if (finger1 < FOC_MAX_FINGERS) {
state->fingers[finger1].x += (char)packet[1];
state->fingers[finger1].y += (char)packet[2];
} else {
psmouse_err(psmouse, "First finger in rel packet invalid: %d\n",
finger1);
}
/*
* If there is an odd number of fingers, the last relative
* packet only contains one finger. In this case, the second
* finger index in the packet is 0 (we subtract 1 in the lines
* above to create array indices, so the finger will overflow
* and be above FOC_MAX_FINGERS).
*/
finger2 = ((packet[3] >> 4) & 0x7) - 1;
if (finger2 < FOC_MAX_FINGERS) {
state->fingers[finger2].x += (char)packet[4];
state->fingers[finger2].y += (char)packet[5];
}
}
static void focaltech_process_packet(struct psmouse *psmouse)
{
unsigned char *packet = psmouse->packet;
switch (packet[0] & 0xf) {
case FOC_TOUCH:
focaltech_process_touch_packet(psmouse, packet);
break;
case FOC_ABS:
focaltech_process_abs_packet(psmouse, packet);
break;
case FOC_REL:
focaltech_process_rel_packet(psmouse, packet);
break;
default:
psmouse_err(psmouse, "Unknown packet type: %02x\n", packet[0]);
break;
}
focaltech_report_state(psmouse);
}
static psmouse_ret_t focaltech_process_byte(struct psmouse *psmouse)
{
if (psmouse->pktcnt >= 6) { /* Full packet received */
focaltech_process_packet(psmouse);
return PSMOUSE_FULL_PACKET;
}
/*
* We might want to do some validation of the data here, but
* we do not know the protocol well enough
*/
return PSMOUSE_GOOD_DATA;
}
static int focaltech_switch_protocol(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[3];
param[0] = 0;
if (ps2_command(ps2dev, param, 0x10f8))
return -EIO;
if (ps2_command(ps2dev, param, 0x10f8))
return -EIO;
if (ps2_command(ps2dev, param, 0x10f8))
return -EIO;
param[0] = 1;
if (ps2_command(ps2dev, param, 0x10f8))
return -EIO;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
return -EIO;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_ENABLE))
return -EIO;
return 0;
}
static void focaltech_disconnect(struct psmouse *psmouse)
{
focaltech_reset(psmouse);
kfree(psmouse->private);
psmouse->private = NULL;
}
static int focaltech_reconnect(struct psmouse *psmouse)
{
int error;
focaltech_reset(psmouse);
error = focaltech_switch_protocol(psmouse);
if (error) {
psmouse_err(psmouse, "Unable to initialize the device\n");
return error;
}
return 0;
}
static void focaltech_set_input_params(struct psmouse *psmouse)
{
struct input_dev *dev = psmouse->dev;
struct focaltech_data *priv = psmouse->private;
/*
* Undo part of setup done for us by psmouse core since touchpad
* is not a relative device.
*/
__clear_bit(EV_REL, dev->evbit);
__clear_bit(REL_X, dev->relbit);
__clear_bit(REL_Y, dev->relbit);
__clear_bit(BTN_RIGHT, dev->keybit);
__clear_bit(BTN_MIDDLE, dev->keybit);
/*
* Now set up our capabilities.
*/
__set_bit(EV_ABS, dev->evbit);
input_set_abs_params(dev, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
input_mt_init_slots(dev, 5, INPUT_MT_POINTER);
__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
}
static int focaltech_read_register(struct ps2dev *ps2dev, int reg,
unsigned char *param)
{
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
return -EIO;
param[0] = 0;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
return -EIO;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
return -EIO;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
return -EIO;
param[0] = reg;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
return -EIO;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
return -EIO;
return 0;
}
static int focaltech_read_size(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
struct focaltech_data *priv = psmouse->private;
char param[3];
if (focaltech_read_register(ps2dev, 2, param))
return -EIO;
/* not sure whether this is 100% correct */
priv->x_max = (unsigned char)param[1] * 128;
priv->y_max = (unsigned char)param[2] * 128;
return 0;
}
int focaltech_init(struct psmouse *psmouse)
{
struct focaltech_data *priv;
int error;
psmouse->private = priv = kzalloc(sizeof(struct focaltech_data),
GFP_KERNEL);
if (!priv)
return -ENOMEM;
focaltech_reset(psmouse);
error = focaltech_read_size(psmouse);
if (error) {
psmouse_err(psmouse,
"Unable to read the size of the touchpad\n");
goto fail;
}
error = focaltech_switch_protocol(psmouse);
if (error) {
psmouse_err(psmouse, "Unable to initialize the device\n");
goto fail;
}
focaltech_set_input_params(psmouse);
psmouse->protocol_handler = focaltech_process_byte;
psmouse->pktsize = 6;
psmouse->disconnect = focaltech_disconnect;
psmouse->reconnect = focaltech_reconnect;
psmouse->cleanup = focaltech_reset;
/* resync is not supported yet */
psmouse->resync_time = 0;
return 0;
fail:
focaltech_reset(psmouse);
kfree(priv);
return error;
}
#else /* CONFIG_MOUSE_PS2_FOCALTECH */
int focaltech_init(struct psmouse *psmouse)
{
focaltech_reset(psmouse);
return 0;
}
#endif /* CONFIG_MOUSE_PS2_FOCALTECH */

View file

@ -2,6 +2,7 @@
* Focaltech TouchPad PS/2 mouse driver
*
* Copyright (c) 2014 Red Hat Inc.
* Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -725,16 +725,19 @@ static int psmouse_extensions(struct psmouse *psmouse,
/* Always check for focaltech, this is safe as it uses pnp-id matching */
if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) {
if (!set_properties || focaltech_init(psmouse) == 0) {
/*
* Not supported yet, use bare protocol.
* Note that we need to also restrict
* psmouse_max_proto so that psmouse_initialize()
* does not try to reset rate and resolution,
* because even that upsets the device.
*/
psmouse_max_proto = PSMOUSE_PS2;
return PSMOUSE_PS2;
if (max_proto > PSMOUSE_IMEX) {
if (!set_properties || focaltech_init(psmouse) == 0) {
if (IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH))
return PSMOUSE_FOCALTECH;
/*
* Note that we need to also restrict
* psmouse_max_proto so that psmouse_initialize()
* does not try to reset rate and resolution,
* because even that upsets the device.
*/
psmouse_max_proto = PSMOUSE_PS2;
return PSMOUSE_PS2;
}
}
}
@ -773,7 +776,7 @@ static int psmouse_extensions(struct psmouse *psmouse,
* Try activating protocol, but check if support is enabled first, since
* we try detecting Synaptics even when protocol is disabled.
*/
if (synaptics_supported() &&
if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) &&
(!set_properties || synaptics_init(psmouse) == 0)) {
return PSMOUSE_SYNAPTICS;
}
@ -798,7 +801,7 @@ static int psmouse_extensions(struct psmouse *psmouse,
*/
if (max_proto > PSMOUSE_IMEX &&
cypress_detect(psmouse, set_properties) == 0) {
if (cypress_supported()) {
if (IS_ENABLED(CONFIG_MOUSE_PS2_CYPRESS)) {
if (cypress_init(psmouse) == 0)
return PSMOUSE_CYPRESS;
@ -1063,6 +1066,15 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.alias = "cortps",
.detect = cortron_detect,
},
#ifdef CONFIG_MOUSE_PS2_FOCALTECH
{
.type = PSMOUSE_FOCALTECH,
.name = "FocalTechPS/2",
.alias = "focaltech",
.detect = focaltech_detect,
.init = focaltech_init,
},
#endif
{
.type = PSMOUSE_AUTO,
.name = "auto",

View file

@ -96,6 +96,7 @@ enum psmouse_type {
PSMOUSE_FSP,
PSMOUSE_SYNAPTICS_RELATIVE,
PSMOUSE_CYPRESS,
PSMOUSE_FOCALTECH,
PSMOUSE_AUTO /* This one should always be last */
};

View file

@ -67,6 +67,9 @@
#define X_MAX_POSITIVE 8176
#define Y_MAX_POSITIVE 8176
/* maximum ABS_MT_POSITION displacement (in mm) */
#define DMAX 10
/*****************************************************************************
* Stuff we need even when we do not want native Synaptics support
****************************************************************************/
@ -575,14 +578,6 @@ static void synaptics_pt_create(struct psmouse *psmouse)
* Functions to interpret the absolute mode packets
****************************************************************************/
static void synaptics_mt_state_set(struct synaptics_mt_state *state, int count,
int sgm, int agm)
{
state->count = count;
state->sgm = sgm;
state->agm = agm;
}
static void synaptics_parse_agm(const unsigned char buf[],
struct synaptics_data *priv,
struct synaptics_hw_state *hw)
@ -601,16 +596,13 @@ static void synaptics_parse_agm(const unsigned char buf[],
break;
case 2:
/* AGM-CONTACT packet: (count, sgm, agm) */
synaptics_mt_state_set(&agm->mt_state, buf[1], buf[2], buf[4]);
/* AGM-CONTACT packet: we are only interested in the count */
priv->agm_count = buf[1];
break;
default:
break;
}
/* Record that at least one AGM has been received since last SGM */
priv->agm_pending = true;
}
static bool is_forcepad;
@ -804,397 +796,13 @@ static void synaptics_report_buttons(struct psmouse *psmouse,
input_report_key(dev, BTN_0 + i, hw->ext_buttons & (1 << i));
}
static void synaptics_report_slot(struct input_dev *dev, int slot,
const struct synaptics_hw_state *hw)
{
input_mt_slot(dev, slot);
input_mt_report_slot_state(dev, MT_TOOL_FINGER, (hw != NULL));
if (!hw)
return;
input_report_abs(dev, ABS_MT_POSITION_X, hw->x);
input_report_abs(dev, ABS_MT_POSITION_Y, synaptics_invert_y(hw->y));
input_report_abs(dev, ABS_MT_PRESSURE, hw->z);
}
static void synaptics_report_mt_data(struct psmouse *psmouse,
struct synaptics_mt_state *mt_state,
const struct synaptics_hw_state *sgm)
const struct synaptics_hw_state *sgm,
int num_fingers)
{
struct input_dev *dev = psmouse->dev;
struct synaptics_data *priv = psmouse->private;
struct synaptics_hw_state *agm = &priv->agm;
struct synaptics_mt_state *old = &priv->mt_state;
switch (mt_state->count) {
case 0:
synaptics_report_slot(dev, 0, NULL);
synaptics_report_slot(dev, 1, NULL);
break;
case 1:
if (mt_state->sgm == -1) {
synaptics_report_slot(dev, 0, NULL);
synaptics_report_slot(dev, 1, NULL);
} else if (mt_state->sgm == 0) {
synaptics_report_slot(dev, 0, sgm);
synaptics_report_slot(dev, 1, NULL);
} else {
synaptics_report_slot(dev, 0, NULL);
synaptics_report_slot(dev, 1, sgm);
}
break;
default:
/*
* If the finger slot contained in SGM is valid, and either
* hasn't changed, or is new, or the old SGM has now moved to
* AGM, then report SGM in MTB slot 0.
* Otherwise, empty MTB slot 0.
*/
if (mt_state->sgm != -1 &&
(mt_state->sgm == old->sgm ||
old->sgm == -1 || mt_state->agm == old->sgm))
synaptics_report_slot(dev, 0, sgm);
else
synaptics_report_slot(dev, 0, NULL);
/*
* If the finger slot contained in AGM is valid, and either
* hasn't changed, or is new, then report AGM in MTB slot 1.
* Otherwise, empty MTB slot 1.
*
* However, in the case where the AGM is new, make sure that
* that it is either the same as the old SGM, or there was no
* SGM.
*
* Otherwise, if the SGM was just 1, and the new AGM is 2, then
* the new AGM will keep the old SGM's tracking ID, which can
* cause apparent drumroll. This happens if in the following
* valid finger sequence:
*
* Action SGM AGM (MTB slot:Contact)
* 1. Touch contact 0 (0:0)
* 2. Touch contact 1 (0:0, 1:1)
* 3. Lift contact 0 (1:1)
* 4. Touch contacts 2,3 (0:2, 1:3)
*
* In step 4, contact 3, in AGM must not be given the same
* tracking ID as contact 1 had in step 3. To avoid this,
* the first agm with contact 3 is dropped and slot 1 is
* invalidated (tracking ID = -1).
*/
if (mt_state->agm != -1 &&
(mt_state->agm == old->agm ||
(old->agm == -1 &&
(old->sgm == -1 || mt_state->agm == old->sgm))))
synaptics_report_slot(dev, 1, agm);
else
synaptics_report_slot(dev, 1, NULL);
break;
}
/* Don't use active slot count to generate BTN_TOOL events. */
input_mt_report_pointer_emulation(dev, false);
/* Send the number of fingers reported by touchpad itself. */
input_mt_report_finger_count(dev, mt_state->count);
synaptics_report_buttons(psmouse, sgm);
input_sync(dev);
}
/* Handle case where mt_state->count = 0 */
static void synaptics_image_sensor_0f(struct synaptics_data *priv,
struct synaptics_mt_state *mt_state)
{
synaptics_mt_state_set(mt_state, 0, -1, -1);
priv->mt_state_lost = false;
}
/* Handle case where mt_state->count = 1 */
static void synaptics_image_sensor_1f(struct synaptics_data *priv,
struct synaptics_mt_state *mt_state)
{
struct synaptics_hw_state *agm = &priv->agm;
struct synaptics_mt_state *old = &priv->mt_state;
/*
* If the last AGM was (0,0,0), and there is only one finger left,
* then we absolutely know that SGM contains slot 0, and all other
* fingers have been removed.
*/
if (priv->agm_pending && agm->z == 0) {
synaptics_mt_state_set(mt_state, 1, 0, -1);
priv->mt_state_lost = false;
return;
}
switch (old->count) {
case 0:
synaptics_mt_state_set(mt_state, 1, 0, -1);
break;
case 1:
/*
* If mt_state_lost, then the previous transition was 3->1,
* and SGM now contains either slot 0 or 1, but we don't know
* which. So, we just assume that the SGM now contains slot 1.
*
* If pending AGM and either:
* (a) the previous SGM slot contains slot 0, or
* (b) there was no SGM slot
* then, the SGM now contains slot 1
*
* Case (a) happens with very rapid "drum roll" gestures, where
* slot 0 finger is lifted and a new slot 1 finger touches
* within one reporting interval.
*
* Case (b) happens if initially two or more fingers tap
* briefly, and all but one lift before the end of the first
* reporting interval.
*
* (In both these cases, slot 0 will becomes empty, so SGM
* contains slot 1 with the new finger)
*
* Else, if there was no previous SGM, it now contains slot 0.
*
* Otherwise, SGM still contains the same slot.
*/
if (priv->mt_state_lost ||
(priv->agm_pending && old->sgm <= 0))
synaptics_mt_state_set(mt_state, 1, 1, -1);
else if (old->sgm == -1)
synaptics_mt_state_set(mt_state, 1, 0, -1);
break;
case 2:
/*
* If mt_state_lost, we don't know which finger SGM contains.
*
* So, report 1 finger, but with both slots empty.
* We will use slot 1 on subsequent 1->1
*/
if (priv->mt_state_lost) {
synaptics_mt_state_set(mt_state, 1, -1, -1);
break;
}
/*
* Since the last AGM was NOT (0,0,0), it was the finger in
* slot 0 that has been removed.
* So, SGM now contains previous AGM's slot, and AGM is now
* empty.
*/
synaptics_mt_state_set(mt_state, 1, old->agm, -1);
break;
case 3:
/*
* Since last AGM was not (0,0,0), we don't know which finger
* is left.
*
* So, report 1 finger, but with both slots empty.
* We will use slot 1 on subsequent 1->1
*/
synaptics_mt_state_set(mt_state, 1, -1, -1);
priv->mt_state_lost = true;
break;
case 4:
case 5:
/* mt_state was updated by AGM-CONTACT packet */
break;
}
}
/* Handle case where mt_state->count = 2 */
static void synaptics_image_sensor_2f(struct synaptics_data *priv,
struct synaptics_mt_state *mt_state)
{
struct synaptics_mt_state *old = &priv->mt_state;
switch (old->count) {
case 0:
synaptics_mt_state_set(mt_state, 2, 0, 1);
break;
case 1:
/*
* If previous SGM contained slot 1 or higher, SGM now contains
* slot 0 (the newly touching finger) and AGM contains SGM's
* previous slot.
*
* Otherwise, SGM still contains slot 0 and AGM now contains
* slot 1.
*/
if (old->sgm >= 1)
synaptics_mt_state_set(mt_state, 2, 0, old->sgm);
else
synaptics_mt_state_set(mt_state, 2, 0, 1);
break;
case 2:
/*
* If mt_state_lost, SGM now contains either finger 1 or 2, but
* we don't know which.
* So, we just assume that the SGM contains slot 0 and AGM 1.
*/
if (priv->mt_state_lost)
synaptics_mt_state_set(mt_state, 2, 0, 1);
/*
* Otherwise, use the same mt_state, since it either hasn't
* changed, or was updated by a recently received AGM-CONTACT
* packet.
*/
break;
case 3:
/*
* 3->2 transitions have two unsolvable problems:
* 1) no indication is given which finger was removed
* 2) no way to tell if agm packet was for finger 3
* before 3->2, or finger 2 after 3->2.
*
* So, report 2 fingers, but empty all slots.
* We will guess slots [0,1] on subsequent 2->2.
*/
synaptics_mt_state_set(mt_state, 2, -1, -1);
priv->mt_state_lost = true;
break;
case 4:
case 5:
/* mt_state was updated by AGM-CONTACT packet */
break;
}
}
/* Handle case where mt_state->count = 3 */
static void synaptics_image_sensor_3f(struct synaptics_data *priv,
struct synaptics_mt_state *mt_state)
{
struct synaptics_mt_state *old = &priv->mt_state;
switch (old->count) {
case 0:
synaptics_mt_state_set(mt_state, 3, 0, 2);
break;
case 1:
/*
* If previous SGM contained slot 2 or higher, SGM now contains
* slot 0 (one of the newly touching fingers) and AGM contains
* SGM's previous slot.
*
* Otherwise, SGM now contains slot 0 and AGM contains slot 2.
*/
if (old->sgm >= 2)
synaptics_mt_state_set(mt_state, 3, 0, old->sgm);
else
synaptics_mt_state_set(mt_state, 3, 0, 2);
break;
case 2:
/*
* If the AGM previously contained slot 3 or higher, then the
* newly touching finger is in the lowest available slot.
*
* If SGM was previously 1 or higher, then the new SGM is
* now slot 0 (with a new finger), otherwise, the new finger
* is now in a hidden slot between 0 and AGM's slot.
*
* In all such cases, the SGM now contains slot 0, and the AGM
* continues to contain the same slot as before.
*/
if (old->agm >= 3) {
synaptics_mt_state_set(mt_state, 3, 0, old->agm);
break;
}
/*
* After some 3->1 and all 3->2 transitions, we lose track
* of which slot is reported by SGM and AGM.
*
* For 2->3 in this state, report 3 fingers, but empty all
* slots, and we will guess (0,2) on a subsequent 0->3.
*
* To userspace, the resulting transition will look like:
* 2:[0,1] -> 3:[-1,-1] -> 3:[0,2]
*/
if (priv->mt_state_lost) {
synaptics_mt_state_set(mt_state, 3, -1, -1);
break;
}
/*
* If the (SGM,AGM) really previously contained slots (0, 1),
* then we cannot know what slot was just reported by the AGM,
* because the 2->3 transition can occur either before or after
* the AGM packet. Thus, this most recent AGM could contain
* either the same old slot 1 or the new slot 2.
* Subsequent AGMs will be reporting slot 2.
*
* To userspace, the resulting transition will look like:
* 2:[0,1] -> 3:[0,-1] -> 3:[0,2]
*/
synaptics_mt_state_set(mt_state, 3, 0, -1);
break;
case 3:
/*
* If, for whatever reason, the previous agm was invalid,
* Assume SGM now contains slot 0, AGM now contains slot 2.
*/
if (old->agm <= 2)
synaptics_mt_state_set(mt_state, 3, 0, 2);
/*
* mt_state either hasn't changed, or was updated by a recently
* received AGM-CONTACT packet.
*/
break;
case 4:
case 5:
/* mt_state was updated by AGM-CONTACT packet */
break;
}
}
/* Handle case where mt_state->count = 4, or = 5 */
static void synaptics_image_sensor_45f(struct synaptics_data *priv,
struct synaptics_mt_state *mt_state)
{
/* mt_state was updated correctly by AGM-CONTACT packet */
priv->mt_state_lost = false;
}
static void synaptics_image_sensor_process(struct psmouse *psmouse,
struct synaptics_hw_state *sgm)
{
struct synaptics_data *priv = psmouse->private;
struct synaptics_hw_state *agm = &priv->agm;
struct synaptics_mt_state mt_state;
/* Initialize using current mt_state (as updated by last agm) */
mt_state = agm->mt_state;
/*
* Update mt_state using the new finger count and current mt_state.
*/
if (sgm->z == 0)
synaptics_image_sensor_0f(priv, &mt_state);
else if (sgm->w >= 4)
synaptics_image_sensor_1f(priv, &mt_state);
else if (sgm->w == 0)
synaptics_image_sensor_2f(priv, &mt_state);
else if (sgm->w == 1 && mt_state.count <= 3)
synaptics_image_sensor_3f(priv, &mt_state);
else
synaptics_image_sensor_45f(priv, &mt_state);
/* Send resulting input events to user space */
synaptics_report_mt_data(psmouse, &mt_state, sgm);
/* Store updated mt_state */
priv->mt_state = agm->mt_state = mt_state;
priv->agm_pending = false;
}
static void synaptics_profile_sensor_process(struct psmouse *psmouse,
struct synaptics_hw_state *sgm,
int num_fingers)
{
struct input_dev *dev = psmouse->dev;
struct synaptics_data *priv = psmouse->private;
struct synaptics_hw_state *hw[2] = { sgm, &priv->agm };
const struct synaptics_hw_state *hw[2] = { sgm, &priv->agm };
struct input_mt_pos pos[2];
int slot[2], nsemi, i;
@ -1205,7 +813,7 @@ static void synaptics_profile_sensor_process(struct psmouse *psmouse,
pos[i].y = synaptics_invert_y(hw[i]->y);
}
input_mt_assign_slots(dev, slot, pos, nsemi);
input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->x_res);
for (i = 0; i < nsemi; i++) {
input_mt_slot(dev, slot[i]);
@ -1216,7 +824,11 @@ static void synaptics_profile_sensor_process(struct psmouse *psmouse,
}
input_mt_drop_unused(dev);
/* Don't use active slot count to generate BTN_TOOL events. */
input_mt_report_pointer_emulation(dev, false);
/* Send the number of fingers reported by touchpad itself. */
input_mt_report_finger_count(dev, num_fingers);
synaptics_report_buttons(psmouse, sgm);
@ -1224,6 +836,30 @@ static void synaptics_profile_sensor_process(struct psmouse *psmouse,
input_sync(dev);
}
static void synaptics_image_sensor_process(struct psmouse *psmouse,
struct synaptics_hw_state *sgm)
{
struct synaptics_data *priv = psmouse->private;
int num_fingers;
/*
* Update mt_state using the new finger count and current mt_state.
*/
if (sgm->z == 0)
num_fingers = 0;
else if (sgm->w >= 4)
num_fingers = 1;
else if (sgm->w == 0)
num_fingers = 2;
else if (sgm->w == 1)
num_fingers = priv->agm_count ? priv->agm_count : 3;
else
num_fingers = 4;
/* Send resulting input events to user space */
synaptics_report_mt_data(psmouse, sgm, num_fingers);
}
/*
* called for each full received packet from the touchpad
*/
@ -1288,7 +924,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
}
if (cr48_profile_sensor) {
synaptics_profile_sensor_process(psmouse, &hw, num_fingers);
synaptics_report_mt_data(psmouse, &hw, num_fingers);
return;
}
@ -1445,7 +1081,7 @@ static void set_input_params(struct psmouse *psmouse,
ABS_MT_POSITION_Y);
/* Image sensors can report per-contact pressure */
input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
input_mt_init_slots(dev, 2, INPUT_MT_POINTER);
input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK);
/* Image sensors can signal 4 and 5 finger clicks */
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
@ -1818,11 +1454,6 @@ int synaptics_init_relative(struct psmouse *psmouse)
return __synaptics_init(psmouse, false);
}
bool synaptics_supported(void)
{
return true;
}
#else /* CONFIG_MOUSE_PS2_SYNAPTICS */
void __init synaptics_module_init(void)
@ -1834,9 +1465,4 @@ int synaptics_init(struct psmouse *psmouse)
return -ENOSYS;
}
bool synaptics_supported(void)
{
return false;
}
#endif /* CONFIG_MOUSE_PS2_SYNAPTICS */

View file

@ -118,16 +118,6 @@
/* amount to fuzz position data when touchpad reports reduced filtering */
#define SYN_REDUCED_FILTER_FUZZ 8
/*
* A structure to describe which internal touchpad finger slots are being
* reported in raw packets.
*/
struct synaptics_mt_state {
int count; /* num fingers being tracked */
int sgm; /* which slot is reported by sgm pkt */
int agm; /* which slot is reported by agm pkt*/
};
/*
* A structure to describe the state of the touchpad hardware (buttons and pad)
*/
@ -143,9 +133,6 @@ struct synaptics_hw_state {
unsigned int down:1;
unsigned char ext_buttons;
signed char scroll;
/* As reported in last AGM-CONTACT packets */
struct synaptics_mt_state mt_state;
};
struct synaptics_data {
@ -170,15 +157,12 @@ struct synaptics_data {
struct serio *pt_port; /* Pass-through serio port */
struct synaptics_mt_state mt_state; /* Current mt finger state */
bool mt_state_lost; /* mt_state may be incorrect */
/*
* Last received Advanced Gesture Mode (AGM) packet. An AGM packet
* contains position data for a second contact, at half resolution.
*/
struct synaptics_hw_state agm;
bool agm_pending; /* new AGM packet received */
unsigned int agm_count; /* finger count reported by agm */
/* ForcePad handling */
unsigned long press_start;
@ -191,6 +175,5 @@ int synaptics_detect(struct psmouse *psmouse, bool set_properties);
int synaptics_init(struct psmouse *psmouse);
int synaptics_init_relative(struct psmouse *psmouse);
void synaptics_reset(struct psmouse *psmouse);
bool synaptics_supported(void);
#endif /* _SYNAPTICS_H */

View file

@ -281,4 +281,14 @@ config HYPERV_KEYBOARD
To compile this driver as a module, choose M here: the module will
be called hyperv_keyboard.
config SERIO_SUN4I_PS2
tristate "Allwinner A10 PS/2 controller support"
depends on ARCH_SUNXI || COMPILE_TEST
help
This selects support for the PS/2 Host Controller on
Allwinner A10.
To compile this driver as a module, choose M here: the
module will be called sun4i-ps2.
endif

View file

@ -29,3 +29,4 @@ obj-$(CONFIG_SERIO_ARC_PS2) += arc_ps2.o
obj-$(CONFIG_SERIO_APBPS2) += apbps2.o
obj-$(CONFIG_SERIO_OLPC_APSP) += olpc_apsp.o
obj-$(CONFIG_HYPERV_KEYBOARD) += hyperv-keyboard.o
obj-$(CONFIG_SERIO_SUN4I_PS2) += sun4i-ps2.o

View file

@ -40,7 +40,6 @@
MODULE_AUTHOR("Laurent Canet <[email protected]>, Thibaut Varene <[email protected]>, Helge Deller <[email protected]>");
MODULE_DESCRIPTION("HP GSC PS2 port driver");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(parisc, gscps2_device_tbl);
#define PFX "gscps2.c: "
@ -439,6 +438,7 @@ static struct parisc_device_id gscps2_device_tbl[] = {
#endif
{ 0, } /* 0 terminated list */
};
MODULE_DEVICE_TABLE(parisc, gscps2_device_tbl);
static struct parisc_driver parisc_ps2_driver = {
.name = "gsc_ps2",

View file

@ -0,0 +1,340 @@
/*
* Driver for Allwinner A10 PS2 host controller
*
* Author: Vishnu Patekar <vishnupatekar0510@gmail.com>
* Aaron.maoye <leafy.myeh@newbietech.com>
*/
#include <linux/module.h>
#include <linux/serio.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#define DRIVER_NAME "sun4i-ps2"
/* register offset definitions */
#define PS2_REG_GCTL 0x00 /* PS2 Module Global Control Reg */
#define PS2_REG_DATA 0x04 /* PS2 Module Data Reg */
#define PS2_REG_LCTL 0x08 /* PS2 Module Line Control Reg */
#define PS2_REG_LSTS 0x0C /* PS2 Module Line Status Reg */
#define PS2_REG_FCTL 0x10 /* PS2 Module FIFO Control Reg */
#define PS2_REG_FSTS 0x14 /* PS2 Module FIFO Status Reg */
#define PS2_REG_CLKDR 0x18 /* PS2 Module Clock Divider Reg*/
/* PS2 GLOBAL CONTROL REGISTER PS2_GCTL */
#define PS2_GCTL_INTFLAG BIT(4)
#define PS2_GCTL_INTEN BIT(3)
#define PS2_GCTL_RESET BIT(2)
#define PS2_GCTL_MASTER BIT(1)
#define PS2_GCTL_BUSEN BIT(0)
/* PS2 LINE CONTROL REGISTER */
#define PS2_LCTL_NOACK BIT(18)
#define PS2_LCTL_TXDTOEN BIT(8)
#define PS2_LCTL_STOPERREN BIT(3)
#define PS2_LCTL_ACKERREN BIT(2)
#define PS2_LCTL_PARERREN BIT(1)
#define PS2_LCTL_RXDTOEN BIT(0)
/* PS2 LINE STATUS REGISTER */
#define PS2_LSTS_TXTDO BIT(8)
#define PS2_LSTS_STOPERR BIT(3)
#define PS2_LSTS_ACKERR BIT(2)
#define PS2_LSTS_PARERR BIT(1)
#define PS2_LSTS_RXTDO BIT(0)
#define PS2_LINE_ERROR_BIT \
(PS2_LSTS_TXTDO | PS2_LSTS_STOPERR | PS2_LSTS_ACKERR | \
PS2_LSTS_PARERR | PS2_LSTS_RXTDO)
/* PS2 FIFO CONTROL REGISTER */
#define PS2_FCTL_TXRST BIT(17)
#define PS2_FCTL_RXRST BIT(16)
#define PS2_FCTL_TXUFIEN BIT(10)
#define PS2_FCTL_TXOFIEN BIT(9)
#define PS2_FCTL_TXRDYIEN BIT(8)
#define PS2_FCTL_RXUFIEN BIT(2)
#define PS2_FCTL_RXOFIEN BIT(1)
#define PS2_FCTL_RXRDYIEN BIT(0)
/* PS2 FIFO STATUS REGISTER */
#define PS2_FSTS_TXUF BIT(10)
#define PS2_FSTS_TXOF BIT(9)
#define PS2_FSTS_TXRDY BIT(8)
#define PS2_FSTS_RXUF BIT(2)
#define PS2_FSTS_RXOF BIT(1)
#define PS2_FSTS_RXRDY BIT(0)
#define PS2_FIFO_ERROR_BIT \
(PS2_FSTS_TXUF | PS2_FSTS_TXOF | PS2_FSTS_RXUF | PS2_FSTS_RXOF)
#define PS2_SAMPLE_CLK 1000000
#define PS2_SCLK 125000
struct sun4i_ps2data {
struct serio *serio;
struct device *dev;
/* IO mapping base */
void __iomem *reg_base;
/* clock management */
struct clk *clk;
/* irq */
spinlock_t lock;
int irq;
};
static irqreturn_t sun4i_ps2_interrupt(int irq, void *dev_id)
{
struct sun4i_ps2data *drvdata = dev_id;
u32 intr_status;
u32 fifo_status;
unsigned char byte;
unsigned int rxflags = 0;
u32 rval;
spin_lock(&drvdata->lock);
/* Get the PS/2 interrupts and clear them */
intr_status = readl(drvdata->reg_base + PS2_REG_LSTS);
fifo_status = readl(drvdata->reg_base + PS2_REG_FSTS);
/* Check line status register */
if (intr_status & PS2_LINE_ERROR_BIT) {
rxflags = (intr_status & PS2_LINE_ERROR_BIT) ? SERIO_FRAME : 0;
rxflags |= (intr_status & PS2_LSTS_PARERR) ? SERIO_PARITY : 0;
rxflags |= (intr_status & PS2_LSTS_PARERR) ? SERIO_TIMEOUT : 0;
rval = PS2_LSTS_TXTDO | PS2_LSTS_STOPERR | PS2_LSTS_ACKERR |
PS2_LSTS_PARERR | PS2_LSTS_RXTDO;
writel(rval, drvdata->reg_base + PS2_REG_LSTS);
}
/* Check FIFO status register */
if (fifo_status & PS2_FIFO_ERROR_BIT) {
rval = PS2_FSTS_TXUF | PS2_FSTS_TXOF | PS2_FSTS_TXRDY |
PS2_FSTS_RXUF | PS2_FSTS_RXOF | PS2_FSTS_RXRDY;
writel(rval, drvdata->reg_base + PS2_REG_FSTS);
}
rval = (fifo_status >> 16) & 0x3;
while (rval--) {
byte = readl(drvdata->reg_base + PS2_REG_DATA) & 0xff;
serio_interrupt(drvdata->serio, byte, rxflags);
}
writel(intr_status, drvdata->reg_base + PS2_REG_LSTS);
writel(fifo_status, drvdata->reg_base + PS2_REG_FSTS);
spin_unlock(&drvdata->lock);
return IRQ_HANDLED;
}
static int sun4i_ps2_open(struct serio *serio)
{
struct sun4i_ps2data *drvdata = serio->port_data;
u32 src_clk = 0;
u32 clk_scdf;
u32 clk_pcdf;
u32 rval;
unsigned long flags;
/* Set line control and enable interrupt */
rval = PS2_LCTL_STOPERREN | PS2_LCTL_ACKERREN
| PS2_LCTL_PARERREN | PS2_LCTL_RXDTOEN;
writel(rval, drvdata->reg_base + PS2_REG_LCTL);
/* Reset FIFO */
rval = PS2_FCTL_TXRST | PS2_FCTL_RXRST | PS2_FCTL_TXUFIEN
| PS2_FCTL_TXOFIEN | PS2_FCTL_RXUFIEN
| PS2_FCTL_RXOFIEN | PS2_FCTL_RXRDYIEN;
writel(rval, drvdata->reg_base + PS2_REG_FCTL);
src_clk = clk_get_rate(drvdata->clk);
/* Set clock divider register */
clk_scdf = src_clk / PS2_SAMPLE_CLK - 1;
clk_pcdf = PS2_SAMPLE_CLK / PS2_SCLK - 1;
rval = (clk_scdf << 8) | clk_pcdf;
writel(rval, drvdata->reg_base + PS2_REG_CLKDR);
/* Set global control register */
rval = PS2_GCTL_RESET | PS2_GCTL_INTEN | PS2_GCTL_MASTER
| PS2_GCTL_BUSEN;
spin_lock_irqsave(&drvdata->lock, flags);
writel(rval, drvdata->reg_base + PS2_REG_GCTL);
spin_unlock_irqrestore(&drvdata->lock, flags);
return 0;
}
static void sun4i_ps2_close(struct serio *serio)
{
struct sun4i_ps2data *drvdata = serio->port_data;
u32 rval;
/* Shut off the interrupt */
rval = readl(drvdata->reg_base + PS2_REG_GCTL);
writel(rval & ~(PS2_GCTL_INTEN), drvdata->reg_base + PS2_REG_GCTL);
synchronize_irq(drvdata->irq);
}
static int sun4i_ps2_write(struct serio *serio, unsigned char val)
{
unsigned long expire = jiffies + msecs_to_jiffies(10000);
struct sun4i_ps2data *drvdata = serio->port_data;
do {
if (readl(drvdata->reg_base + PS2_REG_FSTS) & PS2_FSTS_TXRDY) {
writel(val, drvdata->reg_base + PS2_REG_DATA);
return 0;
}
} while (time_before(jiffies, expire));
return SERIO_TIMEOUT;
}
static int sun4i_ps2_probe(struct platform_device *pdev)
{
struct resource *res; /* IO mem resources */
struct sun4i_ps2data *drvdata;
struct serio *serio;
struct device *dev = &pdev->dev;
unsigned int irq;
int error;
drvdata = kzalloc(sizeof(struct sun4i_ps2data), GFP_KERNEL);
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!drvdata || !serio) {
error = -ENOMEM;
goto err_free_mem;
}
spin_lock_init(&drvdata->lock);
/* IO */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "failed to locate registers\n");
error = -ENXIO;
goto err_free_mem;
}
drvdata->reg_base = ioremap(res->start, resource_size(res));
if (!drvdata->reg_base) {
dev_err(dev, "failed to map registers\n");
error = -ENOMEM;
goto err_free_mem;
}
drvdata->clk = clk_get(dev, NULL);
if (IS_ERR(drvdata->clk)) {
error = PTR_ERR(drvdata->clk);
dev_err(dev, "couldn't get clock %d\n", error);
goto err_ioremap;
}
error = clk_prepare_enable(drvdata->clk);
if (error) {
dev_err(dev, "failed to enable clock %d\n", error);
goto err_clk;
}
serio->id.type = SERIO_8042;
serio->write = sun4i_ps2_write;
serio->open = sun4i_ps2_open;
serio->close = sun4i_ps2_close;
serio->port_data = drvdata;
serio->dev.parent = dev;
strlcpy(serio->name, dev_name(dev), sizeof(serio->name));
strlcpy(serio->phys, dev_name(dev), sizeof(serio->phys));
/* shutoff interrupt */
writel(0, drvdata->reg_base + PS2_REG_GCTL);
/* Get IRQ for the device */
irq = platform_get_irq(pdev, 0);
if (!irq) {
dev_err(dev, "no IRQ found\n");
error = -ENXIO;
goto err_disable_clk;
}
drvdata->irq = irq;
drvdata->serio = serio;
drvdata->dev = dev;
error = request_irq(drvdata->irq, sun4i_ps2_interrupt, 0,
DRIVER_NAME, drvdata);
if (error) {
dev_err(drvdata->dev, "failed to allocate interrupt %d: %d\n",
drvdata->irq, error);
goto err_disable_clk;
}
serio_register_port(serio);
platform_set_drvdata(pdev, drvdata);
return 0; /* success */
err_disable_clk:
clk_disable_unprepare(drvdata->clk);
err_clk:
clk_put(drvdata->clk);
err_ioremap:
iounmap(drvdata->reg_base);
err_free_mem:
kfree(serio);
kfree(drvdata);
return error;
}
static int sun4i_ps2_remove(struct platform_device *pdev)
{
struct sun4i_ps2data *drvdata = platform_get_drvdata(pdev);
serio_unregister_port(drvdata->serio);
free_irq(drvdata->irq, drvdata);
clk_disable_unprepare(drvdata->clk);
clk_put(drvdata->clk);
iounmap(drvdata->reg_base);
kfree(drvdata);
return 0;
}
static const struct of_device_id sun4i_ps2_match[] = {
{ .compatible = "allwinner,sun4i-a10-ps2", },
{ },
};
MODULE_DEVICE_TABLE(of, sun4i_ps2_match);
static struct platform_driver sun4i_ps2_driver = {
.probe = sun4i_ps2_probe,
.remove = sun4i_ps2_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = sun4i_ps2_match,
},
};
module_platform_driver(sun4i_ps2_driver);
MODULE_AUTHOR("Vishnu Patekar <[email protected]>");
MODULE_AUTHOR("Aaron.maoye <[email protected]>");
MODULE_DESCRIPTION("Allwinner A10/Sun4i PS/2 driver");
MODULE_LICENSE("GPL v2");

View file

@ -59,7 +59,7 @@ Scott Hill [email protected]
#include <asm/uaccess.h>
#include <asm/unaligned.h>
#include <asm/byteorder.h>
#include <linux/bitops.h>
#include <linux/usb/input.h>
@ -614,7 +614,6 @@ static void gtco_urb_callback(struct urb *urbinfo)
struct input_dev *inputdev;
int rc;
u32 val = 0;
s8 valsigned = 0;
char le_buffer[2];
inputdev = device->inputdevice;
@ -665,20 +664,11 @@ static void gtco_urb_callback(struct urb *urbinfo)
/* Fall thru */
case 4:
/* Tilt */
input_report_abs(inputdev, ABS_TILT_X,
sign_extend32(device->buffer[6], 6));
/* Sign extend these 7 bit numbers. */
if (device->buffer[6] & 0x40)
device->buffer[6] |= 0x80;
if (device->buffer[7] & 0x40)
device->buffer[7] |= 0x80;
valsigned = (device->buffer[6]);
input_report_abs(inputdev, ABS_TILT_X, (s32)valsigned);
valsigned = (device->buffer[7]);
input_report_abs(inputdev, ABS_TILT_Y, (s32)valsigned);
input_report_abs(inputdev, ABS_TILT_Y,
sign_extend32(device->buffer[7], 6));
/* Fall thru */
case 2:

View file

@ -33,10 +33,8 @@
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/buffer_head.h>
#include <linux/version.h>
#include <linux/slab.h>
#include <linux/firmware.h>
#include <linux/version.h>
#include <linux/input/mt.h>
#include <linux/acpi.h>
#include <linux/of.h>

View file

@ -126,7 +126,7 @@ static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts,
pos[i].y = touch->y;
}
input_mt_assign_slots(ts->input, slots, pos, n);
input_mt_assign_slots(ts->input, slots, pos, n, 0);
}
for (i = 0; i < n; i++) {

View file

@ -34,6 +34,7 @@
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/thermal.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
@ -71,6 +72,9 @@
#define TP_ADC_SELECT(x) ((x) << 3)
#define ADC_CHAN_SELECT(x) ((x) << 0) /* 3 bits */
/* on sun6i, bits 3~6 are left shifted by 1 to 4~7 */
#define SUN6I_TP_MODE_EN(x) ((x) << 5)
/* TP_CTRL2 bits */
#define TP_SENSITIVE_ADJUST(x) ((x) << 28) /* 4 bits */
#define TP_MODE_SELECT(x) ((x) << 26) /* 2 bits */
@ -107,10 +111,13 @@
struct sun4i_ts_data {
struct device *dev;
struct input_dev *input;
struct thermal_zone_device *tz;
void __iomem *base;
unsigned int irq;
bool ignore_fifo_data;
int temp_data;
int temp_offset;
int temp_step;
};
static void sun4i_ts_irq_handle_input(struct sun4i_ts_data *ts, u32 reg_val)
@ -180,16 +187,38 @@ static void sun4i_ts_close(struct input_dev *dev)
writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
}
static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
char *buf)
static int sun4i_get_temp(const struct sun4i_ts_data *ts, long *temp)
{
struct sun4i_ts_data *ts = dev_get_drvdata(dev);
/* No temp_data until the first irq */
if (ts->temp_data == -1)
return -EAGAIN;
return sprintf(buf, "%d\n", (ts->temp_data - 1447) * 100);
*temp = (ts->temp_data - ts->temp_offset) * ts->temp_step;
return 0;
}
static int sun4i_get_tz_temp(void *data, long *temp)
{
return sun4i_get_temp(data, temp);
}
static struct thermal_zone_of_device_ops sun4i_ts_tz_ops = {
.get_temp = sun4i_get_tz_temp,
};
static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct sun4i_ts_data *ts = dev_get_drvdata(dev);
long temp;
int error;
error = sun4i_get_temp(ts, &temp);
if (error)
return error;
return sprintf(buf, "%ld\n", temp);
}
static ssize_t show_temp_label(struct device *dev,
@ -215,6 +244,7 @@ static int sun4i_ts_probe(struct platform_device *pdev)
struct device_node *np = dev->of_node;
struct device *hwmon;
int error;
u32 reg;
bool ts_attached;
ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL);
@ -224,6 +254,25 @@ static int sun4i_ts_probe(struct platform_device *pdev)
ts->dev = dev;
ts->ignore_fifo_data = true;
ts->temp_data = -1;
if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts")) {
/* Allwinner SDK has temperature = -271 + (value / 6) (C) */
ts->temp_offset = 1626;
ts->temp_step = 167;
} else {
/*
* The user manuals do not contain the formula for calculating
* the temperature. The formula used here is from the AXP209,
* which is designed by X-Powers, an affiliate of Allwinner:
*
* temperature = -144.7 + (value * 0.1)
*
* Allwinner does not have any documentation whatsoever for
* this hardware. Moreover, it is claimed that the sensor
* is inaccurate and cannot work properly.
*/
ts->temp_offset = 1447;
ts->temp_step = 100;
}
ts_attached = of_property_read_bool(np, "allwinner,ts-attached");
if (ts_attached) {
@ -280,20 +329,34 @@ static int sun4i_ts_probe(struct platform_device *pdev)
* Set stylus up debounce to aprox 10 ms, enable debounce, and
* finally enable tp mode.
*/
writel(STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1) | TP_MODE_EN(1),
ts->base + TP_CTRL1);
reg = STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1);
if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts"))
reg |= TP_MODE_EN(1);
else
reg |= SUN6I_TP_MODE_EN(1);
writel(reg, ts->base + TP_CTRL1);
/*
* The thermal core does not register hwmon devices for DT-based
* thermal zone sensors, such as this one.
*/
hwmon = devm_hwmon_device_register_with_groups(ts->dev, "sun4i_ts",
ts, sun4i_ts_groups);
if (IS_ERR(hwmon))
return PTR_ERR(hwmon);
ts->tz = thermal_zone_of_sensor_register(ts->dev, 0, ts,
&sun4i_ts_tz_ops);
if (IS_ERR(ts->tz))
ts->tz = NULL;
writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
if (ts_attached) {
error = input_register_device(ts->input);
if (error) {
writel(0, ts->base + TP_INT_FIFOC);
thermal_zone_of_sensor_unregister(ts->dev, ts->tz);
return error;
}
}
@ -310,6 +373,8 @@ static int sun4i_ts_remove(struct platform_device *pdev)
if (ts->input)
input_unregister_device(ts->input);
thermal_zone_of_sensor_unregister(ts->dev, ts->tz);
/* Deactivate all IRQs */
writel(0, ts->base + TP_INT_FIFOC);
@ -318,6 +383,7 @@ static int sun4i_ts_remove(struct platform_device *pdev)
static const struct of_device_id sun4i_ts_of_match[] = {
{ .compatible = "allwinner,sun4i-a10-ts", },
{ .compatible = "allwinner,sun6i-a31-ts", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sun4i_ts_of_match);

View file

@ -26,6 +26,7 @@
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/sort.h>
#include <linux/mfd/ti_am335x_tscadc.h>
@ -52,6 +53,7 @@ struct titsc {
u32 bit_xp, bit_xn, bit_yp, bit_yn;
u32 inp_xp, inp_xn, inp_yp, inp_yn;
u32 step_mask;
u32 charge_delay;
};
static unsigned int titsc_readl(struct titsc *ts, unsigned int reg)
@ -121,7 +123,7 @@ static void titsc_step_config(struct titsc *ts_dev)
{
unsigned int config;
int i;
int end_step;
int end_step, first_step, tsc_steps;
u32 stepenable;
config = STEPCONFIG_MODE_HWSYNC |
@ -140,9 +142,11 @@ static void titsc_step_config(struct titsc *ts_dev)
break;
}
/* 1 … coordinate_readouts is for X */
end_step = ts_dev->coordinate_readouts;
for (i = 0; i < end_step; i++) {
tsc_steps = ts_dev->coordinate_readouts * 2 + 2;
first_step = TOTAL_STEPS - tsc_steps;
/* Steps 16 to 16-coordinate_readouts is for X */
end_step = first_step + tsc_steps;
for (i = end_step - ts_dev->coordinate_readouts; i < end_step; i++) {
titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
}
@ -164,22 +168,20 @@ static void titsc_step_config(struct titsc *ts_dev)
break;
}
/* coordinate_readouts … coordinate_readouts * 2 is for Y */
end_step = ts_dev->coordinate_readouts * 2;
for (i = ts_dev->coordinate_readouts; i < end_step; i++) {
/* 1 ... coordinate_readouts is for Y */
end_step = first_step + ts_dev->coordinate_readouts;
for (i = first_step; i < end_step; i++) {
titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
}
/* Charge step configuration */
config = ts_dev->bit_xp | ts_dev->bit_yn |
STEPCHARGE_RFP_XPUL | STEPCHARGE_RFM_XNUR |
STEPCHARGE_INM_AN1 | STEPCHARGE_INP(ts_dev->inp_yp);
/* Make CHARGECONFIG same as IDLECONFIG */
config = titsc_readl(ts_dev, REG_IDLECONFIG);
titsc_writel(ts_dev, REG_CHARGECONFIG, config);
titsc_writel(ts_dev, REG_CHARGEDELAY, CHARGEDLY_OPENDLY);
titsc_writel(ts_dev, REG_CHARGEDELAY, ts_dev->charge_delay);
/* coordinate_readouts * 2 … coordinate_readouts * 2 + 2 is for Z */
/* coordinate_readouts + 1 ... coordinate_readouts + 2 is for Z */
config = STEPCONFIG_MODE_HWSYNC |
STEPCONFIG_AVG_16 | ts_dev->bit_yp |
ts_dev->bit_xn | STEPCONFIG_INM_ADCREFM |
@ -194,73 +196,104 @@ static void titsc_step_config(struct titsc *ts_dev)
titsc_writel(ts_dev, REG_STEPDELAY(end_step),
STEPCONFIG_OPENDLY);
/* The steps1 … end and bit 0 for TS_Charge */
stepenable = (1 << (end_step + 2)) - 1;
/* The steps end ... end - readouts * 2 + 2 and bit 0 for TS_Charge */
stepenable = 1;
for (i = 0; i < tsc_steps; i++)
stepenable |= 1 << (first_step + i + 1);
ts_dev->step_mask = stepenable;
am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask);
}
static int titsc_cmp_coord(const void *a, const void *b)
{
return *(int *)a - *(int *)b;
}
static void titsc_read_coordinates(struct titsc *ts_dev,
u32 *x, u32 *y, u32 *z1, u32 *z2)
{
unsigned int fifocount = titsc_readl(ts_dev, REG_FIFO0CNT);
unsigned int prev_val_x = ~0, prev_val_y = ~0;
unsigned int prev_diff_x = ~0, prev_diff_y = ~0;
unsigned int read, diff;
unsigned int i, channel;
unsigned int yvals[7], xvals[7];
unsigned int i, xsum = 0, ysum = 0;
unsigned int creads = ts_dev->coordinate_readouts;
*z1 = *z2 = 0;
if (fifocount % (creads * 2 + 2))
fifocount -= fifocount % (creads * 2 + 2);
/*
* Delta filter is used to remove large variations in sampled
* values from ADC. The filter tries to predict where the next
* coordinate could be. This is done by taking a previous
* coordinate and subtracting it form current one. Further the
* algorithm compares the difference with that of a present value,
* if true the value is reported to the sub system.
*/
for (i = 0; i < fifocount; i++) {
read = titsc_readl(ts_dev, REG_FIFO0);
channel = (read & 0xf0000) >> 16;
read &= 0xfff;
if (channel < creads) {
diff = abs(read - prev_val_x);
if (diff < prev_diff_x) {
prev_diff_x = diff;
*x = read;
}
prev_val_x = read;
} else if (channel < creads * 2) {
diff = abs(read - prev_val_y);
if (diff < prev_diff_y) {
prev_diff_y = diff;
*y = read;
}
prev_val_y = read;
} else if (channel < creads * 2 + 1) {
*z1 = read;
} else if (channel < creads * 2 + 2) {
*z2 = read;
}
for (i = 0; i < creads; i++) {
yvals[i] = titsc_readl(ts_dev, REG_FIFO0);
yvals[i] &= 0xfff;
}
*z1 = titsc_readl(ts_dev, REG_FIFO0);
*z1 &= 0xfff;
*z2 = titsc_readl(ts_dev, REG_FIFO0);
*z2 &= 0xfff;
for (i = 0; i < creads; i++) {
xvals[i] = titsc_readl(ts_dev, REG_FIFO0);
xvals[i] &= 0xfff;
}
/*
* If co-ordinates readouts is less than 4 then
* report the average. In case of 4 or more
* readouts, sort the co-ordinate samples, drop
* min and max values and report the average of
* remaining values.
*/
if (creads <= 3) {
for (i = 0; i < creads; i++) {
ysum += yvals[i];
xsum += xvals[i];
}
ysum /= creads;
xsum /= creads;
} else {
sort(yvals, creads, sizeof(unsigned int),
titsc_cmp_coord, NULL);
sort(xvals, creads, sizeof(unsigned int),
titsc_cmp_coord, NULL);
for (i = 1; i < creads - 1; i++) {
ysum += yvals[i];
xsum += xvals[i];
}
ysum /= creads - 2;
xsum /= creads - 2;
}
*y = ysum;
*x = xsum;
}
static irqreturn_t titsc_irq(int irq, void *dev)
{
struct titsc *ts_dev = dev;
struct input_dev *input_dev = ts_dev->input;
unsigned int status, irqclr = 0;
unsigned int fsm, status, irqclr = 0;
unsigned int x = 0, y = 0;
unsigned int z1, z2, z;
unsigned int fsm;
status = titsc_readl(ts_dev, REG_IRQSTATUS);
status = titsc_readl(ts_dev, REG_RAWIRQSTATUS);
if (status & IRQENB_HW_PEN) {
ts_dev->pen_down = true;
titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00);
titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
irqclr |= IRQENB_HW_PEN;
}
if (status & IRQENB_PENUP) {
fsm = titsc_readl(ts_dev, REG_ADCFSM);
if (fsm == ADCFSM_STEPID) {
ts_dev->pen_down = false;
input_report_key(input_dev, BTN_TOUCH, 0);
input_report_abs(input_dev, ABS_PRESSURE, 0);
input_sync(input_dev);
} else {
ts_dev->pen_down = true;
}
irqclr |= IRQENB_PENUP;
}
if (status & IRQENB_EOS)
irqclr |= IRQENB_EOS;
/*
* ADC and touchscreen share the IRQ line.
* FIFO1 interrupts are used by ADC. Handle FIFO0 IRQs here only
@ -291,37 +324,11 @@ static irqreturn_t titsc_irq(int irq, void *dev)
}
irqclr |= IRQENB_FIFO0THRES;
}
/*
* Time for sequencer to settle, to read
* correct state of the sequencer.
*/
udelay(SEQ_SETTLE);
status = titsc_readl(ts_dev, REG_RAWIRQSTATUS);
if (status & IRQENB_PENUP) {
/* Pen up event */
fsm = titsc_readl(ts_dev, REG_ADCFSM);
if (fsm == ADCFSM_STEPID) {
ts_dev->pen_down = false;
input_report_key(input_dev, BTN_TOUCH, 0);
input_report_abs(input_dev, ABS_PRESSURE, 0);
input_sync(input_dev);
} else {
ts_dev->pen_down = true;
}
irqclr |= IRQENB_PENUP;
}
if (status & IRQENB_HW_PEN) {
titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00);
titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
}
if (irqclr) {
titsc_writel(ts_dev, REG_IRQSTATUS, irqclr);
am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask);
if (status & IRQENB_EOS)
am335x_tsc_se_set_cache(ts_dev->mfd_tscadc,
ts_dev->step_mask);
return IRQ_HANDLED;
}
return IRQ_NONE;
@ -368,6 +375,23 @@ static int titsc_parse_dt(struct platform_device *pdev,
if (err < 0)
return err;
if (ts_dev->coordinate_readouts <= 0) {
dev_warn(&pdev->dev,
"invalid co-ordinate readouts, resetting it to 5\n");
ts_dev->coordinate_readouts = 5;
}
err = of_property_read_u32(node, "ti,charge-delay",
&ts_dev->charge_delay);
/*
* If ti,charge-delay value is not specified, then use
* CHARGEDLY_OPENDLY as the default value.
*/
if (err < 0) {
ts_dev->charge_delay = CHARGEDLY_OPENDLY;
dev_warn(&pdev->dev, "ti,charge-delay not specified\n");
}
return of_property_read_u32_array(node, "ti,wire-config",
ts_dev->config_inp, ARRAY_SIZE(ts_dev->config_inp));
}
@ -411,6 +435,7 @@ static int titsc_probe(struct platform_device *pdev)
}
titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES);
titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_EOS);
err = titsc_config_wires(ts_dev);
if (err) {
dev_err(&pdev->dev, "wrong i/p wire configuration\n");