summaryrefslogtreecommitdiffstats
path: root/drivers/input
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2015-11-03 23:45:16 +0100
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2015-11-03 23:45:16 +0100
commit84a73014d86fd660822a20c032625e3afe99ca58 (patch)
tree9e42a2d3c40730947ae18363d00e8a0ee2743792 /drivers/input
parentInput: alps - only the Dell Latitude D420/430/620/630 have separate stick but... (diff)
parentInput: evdev - fix bug in checking duplicate clock change request (diff)
downloadlinux-84a73014d86fd660822a20c032625e3afe99ca58.tar.xz
linux-84a73014d86fd660822a20c032625e3afe99ca58.zip
Merge branch 'next' into for-linus
Prepare first round of input updates for 4.3 merge window.
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/evdev.c270
-rw-r--r--drivers/input/ff-core.c9
-rw-r--r--drivers/input/gameport/gameport.c4
-rw-r--r--drivers/input/input.c25
-rw-r--r--drivers/input/joydev.c22
-rw-r--r--drivers/input/joystick/analog.c4
-rw-r--r--drivers/input/joystick/db9.c113
-rw-r--r--drivers/input/joystick/gamecon.c103
-rw-r--r--drivers/input/joystick/turbografx.c104
-rw-r--r--drivers/input/joystick/walkera0701.c64
-rw-r--r--drivers/input/joystick/xpad.c534
-rw-r--r--drivers/input/keyboard/Kconfig13
-rw-r--r--drivers/input/keyboard/Makefile1
-rw-r--r--drivers/input/keyboard/gpio_keys.c8
-rw-r--r--drivers/input/keyboard/gpio_keys_polled.c88
-rw-r--r--drivers/input/keyboard/nomadik-ske-keypad.c2
-rw-r--r--drivers/input/keyboard/snvs_pwrkey.c226
-rw-r--r--drivers/input/keyboard/tegra-kbc.c5
-rw-r--r--drivers/input/misc/Kconfig30
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/ad714x-i2c.c10
-rw-r--r--drivers/input/misc/ad714x-spi.c10
-rw-r--r--drivers/input/misc/ad714x.c214
-rw-r--r--drivers/input/misc/ad714x.h1
-rw-r--r--drivers/input/misc/da9063_onkey.c129
-rw-r--r--drivers/input/misc/drv260x.c6
-rw-r--r--drivers/input/misc/drv2665.c2
-rw-r--r--drivers/input/misc/drv2667.c4
-rw-r--r--drivers/input/misc/hp_sdc_rtc.c52
-rw-r--r--drivers/input/misc/kxtj9.c3
-rw-r--r--drivers/input/misc/max77693-haptic.c92
-rw-r--r--drivers/input/misc/max77843-haptic.c358
-rw-r--r--drivers/input/misc/rb532_button.c1
-rw-r--r--drivers/input/misc/rotary_encoder.c129
-rw-r--r--drivers/input/misc/xen-kbdfront.c14
-rw-r--r--drivers/input/serio/Kconfig14
-rw-r--r--drivers/input/serio/Makefile1
-rw-r--r--drivers/input/serio/parkbd.c55
-rw-r--r--drivers/input/serio/userio.c285
-rw-r--r--drivers/input/touchscreen/Kconfig24
-rw-r--r--drivers/input/touchscreen/Makefile2
-rw-r--r--drivers/input/touchscreen/ads7846.c4
-rw-r--r--drivers/input/touchscreen/auo-pixcir-ts.c7
-rw-r--r--drivers/input/touchscreen/cyttsp4_i2c.c5
-rw-r--r--drivers/input/touchscreen/edt-ft5x06.c246
-rw-r--r--drivers/input/touchscreen/ft6236.c326
-rw-r--r--drivers/input/touchscreen/ili210x.c4
-rw-r--r--drivers/input/touchscreen/pixcir_i2c_ts.c12
-rw-r--r--drivers/input/touchscreen/rohm_bu21023.c1218
-rw-r--r--drivers/input/touchscreen/sun4i-ts.c8
-rw-r--r--drivers/input/touchscreen/tps6507x-ts.c9
-rw-r--r--drivers/input/touchscreen/wm97xx-core.c13
-rw-r--r--drivers/input/touchscreen/zforce_ts.c7
53 files changed, 3554 insertions, 1337 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 08d496411f75..e9ae3d500a55 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -56,12 +56,57 @@ struct evdev_client {
struct fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
- int clk_type;
+ unsigned int clk_type;
bool revoked;
+ unsigned long *evmasks[EV_CNT];
unsigned int bufsize;
struct input_event buffer[];
};
+static size_t evdev_get_mask_cnt(unsigned int type)
+{
+ static const size_t counts[EV_CNT] = {
+ /* EV_SYN==0 is EV_CNT, _not_ SYN_CNT, see EVIOCGBIT */
+ [EV_SYN] = EV_CNT,
+ [EV_KEY] = KEY_CNT,
+ [EV_REL] = REL_CNT,
+ [EV_ABS] = ABS_CNT,
+ [EV_MSC] = MSC_CNT,
+ [EV_SW] = SW_CNT,
+ [EV_LED] = LED_CNT,
+ [EV_SND] = SND_CNT,
+ [EV_FF] = FF_CNT,
+ };
+
+ return (type < EV_CNT) ? counts[type] : 0;
+}
+
+/* requires the buffer lock to be held */
+static bool __evdev_is_filtered(struct evdev_client *client,
+ unsigned int type,
+ unsigned int code)
+{
+ unsigned long *mask;
+ size_t cnt;
+
+ /* EV_SYN and unknown codes are never filtered */
+ if (type == EV_SYN || type >= EV_CNT)
+ return false;
+
+ /* first test whether the type is filtered */
+ mask = client->evmasks[0];
+ if (mask && !test_bit(type, mask))
+ return true;
+
+ /* unknown values are never filtered */
+ cnt = evdev_get_mask_cnt(type);
+ if (!cnt || code >= cnt)
+ return false;
+
+ mask = client->evmasks[type];
+ return mask && !test_bit(code, mask);
+}
+
/* flush queued events of type @type, caller must hold client->buffer_lock */
static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
{
@@ -146,37 +191,39 @@ static void evdev_queue_syn_dropped(struct evdev_client *client)
static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid)
{
unsigned long flags;
-
- if (client->clk_type == clkid)
- return 0;
+ unsigned int clk_type;
switch (clkid) {
case CLOCK_REALTIME:
- client->clk_type = EV_CLK_REAL;
+ clk_type = EV_CLK_REAL;
break;
case CLOCK_MONOTONIC:
- client->clk_type = EV_CLK_MONO;
+ clk_type = EV_CLK_MONO;
break;
case CLOCK_BOOTTIME:
- client->clk_type = EV_CLK_BOOT;
+ 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->clk_type != clk_type) {
+ client->clk_type = clk_type;
- if (client->head != client->tail) {
- client->packet_head = client->head = client->tail;
- __evdev_queue_syn_dropped(client);
- }
+ /*
+ * Flush pending events and queue SYN_DROPPED event,
+ * but only if the queue is not empty.
+ */
+ spin_lock_irqsave(&client->buffer_lock, flags);
- spin_unlock_irqrestore(&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;
}
@@ -226,12 +273,21 @@ static void evdev_pass_values(struct evdev_client *client,
spin_lock(&client->buffer_lock);
for (v = vals; v != vals + count; v++) {
+ if (__evdev_is_filtered(client, v->type, v->code))
+ continue;
+
+ if (v->type == EV_SYN && v->code == SYN_REPORT) {
+ /* drop empty SYN_REPORT */
+ if (client->packet_head == client->head)
+ continue;
+
+ wakeup = true;
+ }
+
event.type = v->type;
event.code = v->code;
event.value = v->value;
__pass_event(client, &event);
- if (v->type == EV_SYN && v->code == SYN_REPORT)
- wakeup = true;
}
spin_unlock(&client->buffer_lock);
@@ -410,6 +466,7 @@ static int evdev_release(struct inode *inode, struct file *file)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
+ unsigned int i;
mutex_lock(&evdev->mutex);
evdev_ungrab(evdev, client);
@@ -417,6 +474,9 @@ static int evdev_release(struct inode *inode, struct file *file)
evdev_detach_client(evdev, client);
+ for (i = 0; i < EV_CNT; ++i)
+ kfree(client->evmasks[i]);
+
kvfree(client);
evdev_close_device(evdev);
@@ -627,7 +687,46 @@ static int bits_to_user(unsigned long *bits, unsigned int maxbit,
return len;
}
+
+static int bits_from_user(unsigned long *bits, unsigned int maxbit,
+ unsigned int maxlen, const void __user *p, int compat)
+{
+ int len, i;
+
+ if (compat) {
+ if (maxlen % sizeof(compat_long_t))
+ return -EINVAL;
+
+ len = BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t);
+ if (len > maxlen)
+ len = maxlen;
+
+ for (i = 0; i < len / sizeof(compat_long_t); i++)
+ if (copy_from_user((compat_long_t *) bits +
+ i + 1 - ((i % 2) << 1),
+ (compat_long_t __user *) p + i,
+ sizeof(compat_long_t)))
+ return -EFAULT;
+ if (i % 2)
+ *((compat_long_t *) bits + i - 1) = 0;
+
+ } else {
+ if (maxlen % sizeof(long))
+ return -EINVAL;
+
+ len = BITS_TO_LONGS(maxbit) * sizeof(long);
+ if (len > maxlen)
+ len = maxlen;
+
+ if (copy_from_user(bits, p, len))
+ return -EFAULT;
+ }
+
+ return len;
+}
+
#else
+
static int bits_to_user(unsigned long *bits, unsigned int maxbit,
unsigned int maxlen, void __user *p, int compat)
{
@@ -640,6 +739,24 @@ static int bits_to_user(unsigned long *bits, unsigned int maxbit,
return copy_to_user(p, bits, len) ? -EFAULT : len;
}
+
+static int bits_from_user(unsigned long *bits, unsigned int maxbit,
+ unsigned int maxlen, const void __user *p, int compat)
+{
+ size_t chunk_size = compat ? sizeof(compat_long_t) : sizeof(long);
+ int len;
+
+ if (maxlen % chunk_size)
+ return -EINVAL;
+
+ len = compat ? BITS_TO_LONGS_COMPAT(maxbit) : BITS_TO_LONGS(maxbit);
+ len *= chunk_size;
+ if (len > maxlen)
+ len = maxlen;
+
+ return copy_from_user(bits, p, len) ? -EFAULT : len;
+}
+
#endif /* __BIG_ENDIAN */
#else
@@ -655,6 +772,21 @@ static int bits_to_user(unsigned long *bits, unsigned int maxbit,
return copy_to_user(p, bits, len) ? -EFAULT : len;
}
+static int bits_from_user(unsigned long *bits, unsigned int maxbit,
+ unsigned int maxlen, const void __user *p, int compat)
+{
+ int len;
+
+ if (maxlen % sizeof(long))
+ return -EINVAL;
+
+ len = BITS_TO_LONGS(maxbit) * sizeof(long);
+ if (len > maxlen)
+ len = maxlen;
+
+ return copy_from_user(bits, p, len) ? -EFAULT : len;
+}
+
#endif /* CONFIG_COMPAT */
static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
@@ -849,6 +981,81 @@ static int evdev_revoke(struct evdev *evdev, struct evdev_client *client,
return 0;
}
+/* must be called with evdev-mutex held */
+static int evdev_set_mask(struct evdev_client *client,
+ unsigned int type,
+ const void __user *codes,
+ u32 codes_size,
+ int compat)
+{
+ unsigned long flags, *mask, *oldmask;
+ size_t cnt;
+ int error;
+
+ /* we allow unknown types and 'codes_size > size' for forward-compat */
+ cnt = evdev_get_mask_cnt(type);
+ if (!cnt)
+ return 0;
+
+ mask = kcalloc(sizeof(unsigned long), BITS_TO_LONGS(cnt), GFP_KERNEL);
+ if (!mask)
+ return -ENOMEM;
+
+ error = bits_from_user(mask, cnt - 1, codes_size, codes, compat);
+ if (error < 0) {
+ kfree(mask);
+ return error;
+ }
+
+ spin_lock_irqsave(&client->buffer_lock, flags);
+ oldmask = client->evmasks[type];
+ client->evmasks[type] = mask;
+ spin_unlock_irqrestore(&client->buffer_lock, flags);
+
+ kfree(oldmask);
+
+ return 0;
+}
+
+/* must be called with evdev-mutex held */
+static int evdev_get_mask(struct evdev_client *client,
+ unsigned int type,
+ void __user *codes,
+ u32 codes_size,
+ int compat)
+{
+ unsigned long *mask;
+ size_t cnt, size, xfer_size;
+ int i;
+ int error;
+
+ /* we allow unknown types and 'codes_size > size' for forward-compat */
+ cnt = evdev_get_mask_cnt(type);
+ size = sizeof(unsigned long) * BITS_TO_LONGS(cnt);
+ xfer_size = min_t(size_t, codes_size, size);
+
+ if (cnt > 0) {
+ mask = client->evmasks[type];
+ if (mask) {
+ error = bits_to_user(mask, cnt - 1,
+ xfer_size, codes, compat);
+ if (error < 0)
+ return error;
+ } else {
+ /* fake mask with all bits set */
+ for (i = 0; i < xfer_size; i++)
+ if (put_user(0xffU, (u8 __user *)codes + i))
+ return -EFAULT;
+ }
+ }
+
+ if (xfer_size < codes_size)
+ if (clear_user(codes + xfer_size, codes_size - xfer_size))
+ return -EFAULT;
+
+ return 0;
+}
+
static long evdev_do_ioctl(struct file *file, unsigned int cmd,
void __user *p, int compat_mode)
{
@@ -856,6 +1063,7 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
struct evdev *evdev = client->evdev;
struct input_dev *dev = evdev->handle.dev;
struct input_absinfo abs;
+ struct input_mask mask;
struct ff_effect effect;
int __user *ip = (int __user *)p;
unsigned int i, t, u, v;
@@ -917,6 +1125,30 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
else
return evdev_revoke(evdev, client, file);
+ case EVIOCGMASK: {
+ void __user *codes_ptr;
+
+ if (copy_from_user(&mask, p, sizeof(mask)))
+ return -EFAULT;
+
+ codes_ptr = (void __user *)(unsigned long)mask.codes_ptr;
+ return evdev_get_mask(client,
+ mask.type, codes_ptr, mask.codes_size,
+ compat_mode);
+ }
+
+ case EVIOCSMASK: {
+ const void __user *codes_ptr;
+
+ if (copy_from_user(&mask, p, sizeof(mask)))
+ return -EFAULT;
+
+ codes_ptr = (const void __user *)(unsigned long)mask.codes_ptr;
+ return evdev_set_mask(client,
+ mask.type, codes_ptr, mask.codes_size,
+ compat_mode);
+ }
+
case EVIOCSCLOCKID:
if (copy_from_user(&i, p, sizeof(unsigned int)))
return -EFAULT;
diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c
index c64208267198..8f2042432c85 100644
--- a/drivers/input/ff-core.c
+++ b/drivers/input/ff-core.c
@@ -273,14 +273,14 @@ int input_ff_event(struct input_dev *dev, unsigned int type,
switch (code) {
case FF_GAIN:
- if (!test_bit(FF_GAIN, dev->ffbit) || value > 0xffff)
+ if (!test_bit(FF_GAIN, dev->ffbit) || value > 0xffffU)
break;
ff->set_gain(dev, value);
break;
case FF_AUTOCENTER:
- if (!test_bit(FF_AUTOCENTER, dev->ffbit) || value > 0xffff)
+ if (!test_bit(FF_AUTOCENTER, dev->ffbit) || value > 0xffffU)
break;
ff->set_autocenter(dev, value);
@@ -318,6 +318,11 @@ int input_ff_create(struct input_dev *dev, unsigned int max_effects)
return -EINVAL;
}
+ if (max_effects > FF_MAX_EFFECTS) {
+ dev_err(&dev->dev, "cannot allocate more than FF_MAX_EFFECTS effects\n");
+ return -EINVAL;
+ }
+
ff_dev_size = sizeof(struct ff_device) +
max_effects * sizeof(struct file *);
if (ff_dev_size < max_effects) /* overflow */
diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c
index e853a2134680..4a2a9e370be7 100644
--- a/drivers/input/gameport/gameport.c
+++ b/drivers/input/gameport/gameport.c
@@ -149,9 +149,9 @@ static int old_gameport_measure_speed(struct gameport *gameport)
for(i = 0; i < 50; i++) {
local_irq_save(flags);
- rdtscl(t1);
+ t1 = rdtsc();
for (t = 0; t < 50; t++) gameport_read(gameport);
- rdtscl(t2);
+ t2 = rdtsc();
local_irq_restore(flags);
udelay(i * 10);
if (t2 - t1 < tx) tx = t2 - t1;
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 5391abd28b27..880605959aa6 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -2045,6 +2045,23 @@ static void devm_input_device_unregister(struct device *dev, void *res)
}
/**
+ * input_enable_softrepeat - enable software autorepeat
+ * @dev: input device
+ * @delay: repeat delay
+ * @period: repeat period
+ *
+ * Enable software autorepeat on the input device.
+ */
+void input_enable_softrepeat(struct input_dev *dev, int delay, int period)
+{
+ dev->timer.data = (unsigned long) dev;
+ dev->timer.function = input_repeat_key;
+ dev->rep[REP_DELAY] = delay;
+ dev->rep[REP_PERIOD] = period;
+}
+EXPORT_SYMBOL(input_enable_softrepeat);
+
+/**
* input_register_device - register device with input core
* @dev: device to be registered
*
@@ -2108,12 +2125,8 @@ int input_register_device(struct input_dev *dev)
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
- if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
- dev->timer.data = (long) dev;
- dev->timer.function = input_repeat_key;
- dev->rep[REP_DELAY] = 250;
- dev->rep[REP_PERIOD] = 33;
- }
+ if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD])
+ input_enable_softrepeat(dev, 250, 33);
if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
index 6cb5a3e5f9a1..5d11fea3c8ec 100644
--- a/drivers/input/joydev.c
+++ b/drivers/input/joydev.c
@@ -444,14 +444,9 @@ static int joydev_handle_JSIOCSAXMAP(struct joydev *joydev,
len = min(len, sizeof(joydev->abspam));
/* Validate the map. */
- abspam = kmalloc(len, GFP_KERNEL);
- if (!abspam)
- return -ENOMEM;
-
- if (copy_from_user(abspam, argp, len)) {
- retval = -EFAULT;
- goto out;
- }
+ abspam = memdup_user(argp, len);
+ if (IS_ERR(abspam))
+ return PTR_ERR(abspam);
for (i = 0; i < joydev->nabs; i++) {
if (abspam[i] > ABS_MAX) {
@@ -480,14 +475,9 @@ static int joydev_handle_JSIOCSBTNMAP(struct joydev *joydev,
len = min(len, sizeof(joydev->keypam));
/* Validate the map. */
- keypam = kmalloc(len, GFP_KERNEL);
- if (!keypam)
- return -ENOMEM;
-
- if (copy_from_user(keypam, argp, len)) {
- retval = -EFAULT;
- goto out;
- }
+ keypam = memdup_user(argp, len);
+ if (IS_ERR(keypam))
+ return PTR_ERR(keypam);
for (i = 0; i < joydev->nkey; i++) {
if (keypam[i] > KEY_MAX || keypam[i] < BTN_MISC) {
diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c
index 4284080e481d..6f8b084e13d0 100644
--- a/drivers/input/joystick/analog.c
+++ b/drivers/input/joystick/analog.c
@@ -143,7 +143,7 @@ struct analog_port {
#include <linux/i8253.h>
-#define GET_TIME(x) do { if (cpu_has_tsc) rdtscl(x); else x = get_time_pit(); } while (0)
+#define GET_TIME(x) do { if (cpu_has_tsc) x = (unsigned int)rdtsc(); else x = get_time_pit(); } while (0)
#define DELTA(x,y) (cpu_has_tsc ? ((y) - (x)) : ((x) - (y) + ((x) < (y) ? PIT_TICK_RATE / HZ : 0)))
#define TIME_NAME (cpu_has_tsc?"TSC":"PIT")
static unsigned int get_time_pit(void)
@@ -160,7 +160,7 @@ static unsigned int get_time_pit(void)
return count;
}
#elif defined(__x86_64__)
-#define GET_TIME(x) rdtscl(x)
+#define GET_TIME(x) do { x = (unsigned int)rdtsc(); } while (0)
#define DELTA(x,y) ((y)-(x))
#define TIME_NAME "TSC"
#elif defined(__alpha__) || defined(CONFIG_MN10300) || defined(CONFIG_ARM) || defined(CONFIG_ARM64) || defined(CONFIG_TILE)
diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c
index 8e7de5c7754f..932d07307454 100644
--- a/drivers/input/joystick/db9.c
+++ b/drivers/input/joystick/db9.c
@@ -48,7 +48,7 @@ struct db9_config {
};
#define DB9_MAX_PORTS 3
-static struct db9_config db9_cfg[DB9_MAX_PORTS] __initdata;
+static struct db9_config db9_cfg[DB9_MAX_PORTS];
module_param_array_named(dev, db9_cfg[0].args, int, &db9_cfg[0].nargs, 0);
MODULE_PARM_DESC(dev, "Describes first attached device (<parport#>,<type>)");
@@ -106,6 +106,7 @@ struct db9 {
struct pardevice *pd;
int mode;
int used;
+ int parportno;
struct mutex mutex;
char phys[DB9_MAX_DEVICES][32];
};
@@ -553,54 +554,60 @@ static void db9_close(struct input_dev *dev)
mutex_unlock(&db9->mutex);
}
-static struct db9 __init *db9_probe(int parport, int mode)
+static void db9_attach(struct parport *pp)
{
struct db9 *db9;
const struct db9_mode_data *db9_mode;
- struct parport *pp;
struct pardevice *pd;
struct input_dev *input_dev;
- int i, j;
- int err;
+ int i, j, port_idx;
+ int mode;
+ struct pardev_cb db9_parport_cb;
+
+ for (port_idx = 0; port_idx < DB9_MAX_PORTS; port_idx++) {
+ if (db9_cfg[port_idx].nargs == 0 ||
+ db9_cfg[port_idx].args[DB9_ARG_PARPORT] < 0)
+ continue;
+
+ if (db9_cfg[port_idx].args[DB9_ARG_PARPORT] == pp->number)
+ break;
+ }
+
+ if (port_idx == DB9_MAX_PORTS) {
+ pr_debug("Not using parport%d.\n", pp->number);
+ return;
+ }
+
+ mode = db9_cfg[port_idx].args[DB9_ARG_MODE];
if (mode < 1 || mode >= DB9_MAX_PAD || !db9_modes[mode].n_buttons) {
printk(KERN_ERR "db9.c: Bad device type %d\n", mode);
- err = -EINVAL;
- goto err_out;
+ return;
}
db9_mode = &db9_modes[mode];
- pp = parport_find_number(parport);
- if (!pp) {
- printk(KERN_ERR "db9.c: no such parport\n");
- err = -ENODEV;
- goto err_out;
- }
-
if (db9_mode->bidirectional && !(pp->modes & PARPORT_MODE_TRISTATE)) {
printk(KERN_ERR "db9.c: specified parport is not bidirectional\n");
- err = -EINVAL;
- goto err_put_pp;
+ return;
}
- pd = parport_register_device(pp, "db9", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL);
+ db9_parport_cb.flags = PARPORT_FLAG_EXCL;
+
+ pd = parport_register_dev_model(pp, "db9", &db9_parport_cb, port_idx);
if (!pd) {
printk(KERN_ERR "db9.c: parport busy already - lp.o loaded?\n");
- err = -EBUSY;
- goto err_put_pp;
+ return;
}
db9 = kzalloc(sizeof(struct db9), GFP_KERNEL);
- if (!db9) {
- printk(KERN_ERR "db9.c: Not enough memory\n");
- err = -ENOMEM;
+ if (!db9)
goto err_unreg_pardev;
- }
mutex_init(&db9->mutex);
db9->pd = pd;
db9->mode = mode;
+ db9->parportno = pp->number;
init_timer(&db9->timer);
db9->timer.data = (long) db9;
db9->timer.function = db9_timer;
@@ -610,7 +617,6 @@ static struct db9 __init *db9_probe(int parport, int mode)
db9->dev[i] = input_dev = input_allocate_device();
if (!input_dev) {
printk(KERN_ERR "db9.c: Not enough memory for input device\n");
- err = -ENOMEM;
goto err_unreg_devs;
}
@@ -639,13 +645,12 @@ static struct db9 __init *db9_probe(int parport, int mode)
input_set_abs_params(input_dev, db9_abs[j], 1, 255, 0, 0);
}
- err = input_register_device(input_dev);
- if (err)
+ if (input_register_device(input_dev))
goto err_free_dev;
}
- parport_put_port(pp);
- return db9;
+ db9_base[port_idx] = db9;
+ return;
err_free_dev:
input_free_device(db9->dev[i]);
@@ -655,15 +660,23 @@ static struct db9 __init *db9_probe(int parport, int mode)
kfree(db9);
err_unreg_pardev:
parport_unregister_device(pd);
- err_put_pp:
- parport_put_port(pp);
- err_out:
- return ERR_PTR(err);
}
-static void db9_remove(struct db9 *db9)
+static void db9_detach(struct parport *port)
{
int i;
+ struct db9 *db9;
+
+ for (i = 0; i < DB9_MAX_PORTS; i++) {
+ if (db9_base[i] && db9_base[i]->parportno == port->number)
+ break;
+ }
+
+ if (i == DB9_MAX_PORTS)
+ return;
+
+ db9 = db9_base[i];
+ db9_base[i] = NULL;
for (i = 0; i < min(db9_modes[db9->mode].n_pads, DB9_MAX_DEVICES); i++)
input_unregister_device(db9->dev[i]);
@@ -671,11 +684,17 @@ static void db9_remove(struct db9 *db9)
kfree(db9);
}
+static struct parport_driver db9_parport_driver = {
+ .name = "db9",
+ .match_port = db9_attach,
+ .detach = db9_detach,
+ .devmodel = true,
+};
+
static int __init db9_init(void)
{
int i;
int have_dev = 0;
- int err = 0;
for (i = 0; i < DB9_MAX_PORTS; i++) {
if (db9_cfg[i].nargs == 0 || db9_cfg[i].args[DB9_ARG_PARPORT] < 0)
@@ -683,37 +702,21 @@ static int __init db9_init(void)
if (db9_cfg[i].nargs < 2) {
printk(KERN_ERR "db9.c: Device type must be specified.\n");
- err = -EINVAL;
- break;
- }
-
- db9_base[i] = db9_probe(db9_cfg[i].args[DB9_ARG_PARPORT],
- db9_cfg[i].args[DB9_ARG_MODE]);
- if (IS_ERR(db9_base[i])) {
- err = PTR_ERR(db9_base[i]);
- break;
+ return -EINVAL;
}
have_dev = 1;
}
- if (err) {
- while (--i >= 0)
- if (db9_base[i])
- db9_remove(db9_base[i]);
- return err;
- }
+ if (!have_dev)
+ return -ENODEV;
- return have_dev ? 0 : -ENODEV;
+ return parport_register_driver(&db9_parport_driver);
}
static void __exit db9_exit(void)
{
- int i;
-
- for (i = 0; i < DB9_MAX_PORTS; i++)
- if (db9_base[i])
- db9_remove(db9_base[i]);
+ parport_unregister_driver(&db9_parport_driver);
}
module_init(db9_init);
diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c
index e68e49786483..5a672dcac0d8 100644
--- a/drivers/input/joystick/gamecon.c
+++ b/drivers/input/joystick/gamecon.c
@@ -53,7 +53,7 @@ struct gc_config {
unsigned int nargs;
};
-static struct gc_config gc_cfg[GC_MAX_PORTS] __initdata;
+static struct gc_config gc_cfg[GC_MAX_PORTS];
module_param_array_named(map, gc_cfg[0].args, int, &gc_cfg[0].nargs, 0);
MODULE_PARM_DESC(map, "Describes first set of devices (<parport#>,<pad1>,<pad2>,..<pad5>)");
@@ -92,6 +92,7 @@ struct gc {
struct timer_list timer;
int pad_count[GC_MAX];
int used;
+ int parportno;
struct mutex mutex;
};
@@ -304,7 +305,7 @@ static int gc_n64_play_effect(struct input_dev *dev, void *data,
return 0;
}
-static int __init gc_n64_init_ff(struct input_dev *dev, int i)
+static int gc_n64_init_ff(struct input_dev *dev, int i)
{
struct gc_subdev *sdev;
int err;
@@ -811,7 +812,7 @@ static void gc_close(struct input_dev *dev)
mutex_unlock(&gc->mutex);
}
-static int __init gc_setup_pad(struct gc *gc, int idx, int pad_type)
+static int gc_setup_pad(struct gc *gc, int idx, int pad_type)
{
struct gc_pad *pad = &gc->pads[idx];
struct input_dev *input_dev;
@@ -926,46 +927,55 @@ err_free_dev:
return err;
}
-static struct gc __init *gc_probe(int parport, int *pads, int n_pads)
+static void gc_attach(struct parport *pp)
{
struct gc *gc;
- struct parport *pp;
struct pardevice *pd;
- int i;
+ int i, port_idx;
int count = 0;
- int err;
+ int *pads, n_pads;
+ struct pardev_cb gc_parport_cb;
+
+ for (port_idx = 0; port_idx < GC_MAX_PORTS; port_idx++) {
+ if (gc_cfg[port_idx].nargs == 0 || gc_cfg[port_idx].args[0] < 0)
+ continue;
+
+ if (gc_cfg[port_idx].args[0] == pp->number)
+ break;
+ }
- pp = parport_find_number(parport);
- if (!pp) {
- pr_err("no such parport %d\n", parport);
- err = -EINVAL;
- goto err_out;
+ if (port_idx == GC_MAX_PORTS) {
+ pr_debug("Not using parport%d.\n", pp->number);
+ return;
}
+ pads = gc_cfg[port_idx].args + 1;
+ n_pads = gc_cfg[port_idx].nargs - 1;
- pd = parport_register_device(pp, "gamecon", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL);
+ gc_parport_cb.flags = PARPORT_FLAG_EXCL;
+
+ pd = parport_register_dev_model(pp, "gamecon", &gc_parport_cb,
+ port_idx);
if (!pd) {
pr_err("parport busy already - lp.o loaded?\n");
- err = -EBUSY;
- goto err_put_pp;
+ return;
}
gc = kzalloc(sizeof(struct gc), GFP_KERNEL);
if (!gc) {
pr_err("Not enough memory\n");
- err = -ENOMEM;
goto err_unreg_pardev;
}
mutex_init(&gc->mutex);
gc->pd = pd;
+ gc->parportno = pp->number;
setup_timer(&gc->timer, gc_timer, (long) gc);
for (i = 0; i < n_pads && i < GC_MAX_DEVICES; i++) {
if (!pads[i])
continue;
- err = gc_setup_pad(gc, i, pads[i]);
- if (err)
+ if (gc_setup_pad(gc, i, pads[i]))
goto err_unreg_devs;
count++;
@@ -973,12 +983,11 @@ static struct gc __init *gc_probe(int parport, int *pads, int n_pads)
if (count == 0) {
pr_err("No valid devices specified\n");
- err = -EINVAL;
goto err_free_gc;
}
- parport_put_port(pp);
- return gc;
+ gc_base[port_idx] = gc;
+ return;
err_unreg_devs:
while (--i >= 0)
@@ -988,15 +997,23 @@ static struct gc __init *gc_probe(int parport, int *pads, int n_pads)
kfree(gc);
err_unreg_pardev:
parport_unregister_device(pd);
- err_put_pp:
- parport_put_port(pp);
- err_out:
- return ERR_PTR(err);
}
-static void gc_remove(struct gc *gc)
+static void gc_detach(struct parport *port)
{
int i;
+ struct gc *gc;
+
+ for (i = 0; i < GC_MAX_PORTS; i++) {
+ if (gc_base[i] && gc_base[i]->parportno == port->number)
+ break;
+ }
+
+ if (i == GC_MAX_PORTS)
+ return;
+
+ gc = gc_base[i];
+ gc_base[i] = NULL;
for (i = 0; i < GC_MAX_DEVICES; i++)
if (gc->pads[i].dev)
@@ -1005,11 +1022,17 @@ static void gc_remove(struct gc *gc)
kfree(gc);
}
+static struct parport_driver gc_parport_driver = {
+ .name = "gamecon",
+ .match_port = gc_attach,
+ .detach = gc_detach,
+ .devmodel = true,
+};
+
static int __init gc_init(void)
{
int i;
int have_dev = 0;
- int err = 0;
for (i = 0; i < GC_MAX_PORTS; i++) {
if (gc_cfg[i].nargs == 0 || gc_cfg[i].args[0] < 0)
@@ -1017,37 +1040,21 @@ static int __init gc_init(void)
if (gc_cfg[i].nargs < 2) {
pr_err("at least one device must be specified\n");
- err = -EINVAL;
- break;
- }
-
- gc_base[i] = gc_probe(gc_cfg[i].args[0],
- gc_cfg[i].args + 1, gc_cfg[i].nargs - 1);
- if (IS_ERR(gc_base[i])) {
- err = PTR_ERR(gc_base[i]);
- break;
+ return -EINVAL;
}
have_dev = 1;
}
- if (err) {
- while (--i >= 0)
- if (gc_base[i])
- gc_remove(gc_base[i]);
- return err;
- }
+ if (!have_dev)
+ return -ENODEV;
- return have_dev ? 0 : -ENODEV;
+ return parport_register_driver(&gc_parport_driver);
}
static void __exit gc_exit(void)
{
- int i;
-
- for (i = 0; i < GC_MAX_PORTS; i++)
- if (gc_base[i])
- gc_remove(gc_base[i]);
+ parport_unregister_driver(&gc_parport_driver);
}
module_init(gc_init);
diff --git a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c
index 891797ad76bc..9f5bca26bd2f 100644
--- a/drivers/input/joystick/turbografx.c
+++ b/drivers/input/joystick/turbografx.c
@@ -49,7 +49,7 @@ struct tgfx_config {
unsigned int nargs;
};
-static struct tgfx_config tgfx_cfg[TGFX_MAX_PORTS] __initdata;
+static struct tgfx_config tgfx_cfg[TGFX_MAX_PORTS];
module_param_array_named(map, tgfx_cfg[0].args, int, &tgfx_cfg[0].nargs, 0);
MODULE_PARM_DESC(map, "Describes first set of devices (<parport#>,<js1>,<js2>,..<js7>");
@@ -81,6 +81,7 @@ static struct tgfx {
char phys[TGFX_MAX_DEVICES][32];
int sticks;
int used;
+ int parportno;
struct mutex sem;
} *tgfx_base[TGFX_MAX_PORTS];
@@ -156,38 +157,48 @@ static void tgfx_close(struct input_dev *dev)
* tgfx_probe() probes for tg gamepads.
*/
-static struct tgfx __init *tgfx_probe(int parport, int *n_buttons, int n_devs)
+static void tgfx_attach(struct parport *pp)
{
struct tgfx *tgfx;
struct input_dev *input_dev;
- struct parport *pp;
struct pardevice *pd;
- int i, j;
- int err;
+ int i, j, port_idx;
+ int *n_buttons, n_devs;
+ struct pardev_cb tgfx_parport_cb;
+
+ for (port_idx = 0; port_idx < TGFX_MAX_PORTS; port_idx++) {
+ if (tgfx_cfg[port_idx].nargs == 0 ||
+ tgfx_cfg[port_idx].args[0] < 0)
+ continue;
+ if (tgfx_cfg[port_idx].args[0] == pp->number)
+ break;
+ }
- pp = parport_find_number(parport);
- if (!pp) {
- printk(KERN_ERR "turbografx.c: no such parport\n");
- err = -EINVAL;
- goto err_out;
+ if (port_idx == TGFX_MAX_PORTS) {
+ pr_debug("Not using parport%d.\n", pp->number);
+ return;
}
+ n_buttons = tgfx_cfg[port_idx].args + 1;
+ n_devs = tgfx_cfg[port_idx].nargs - 1;
+
+ tgfx_parport_cb.flags = PARPORT_FLAG_EXCL;
- pd = parport_register_device(pp, "turbografx", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL);
+ pd = parport_register_dev_model(pp, "turbografx", &tgfx_parport_cb,
+ port_idx);
if (!pd) {
- printk(KERN_ERR "turbografx.c: parport busy already - lp.o loaded?\n");
- err = -EBUSY;
- goto err_put_pp;
+ pr_err("parport busy already - lp.o loaded?\n");
+ return;
}
tgfx = kzalloc(sizeof(struct tgfx), GFP_KERNEL);
if (!tgfx) {
printk(KERN_ERR "turbografx.c: Not enough memory\n");
- err = -ENOMEM;
goto err_unreg_pardev;
}
mutex_init(&tgfx->sem);
tgfx->pd = pd;
+ tgfx->parportno = pp->number;
init_timer(&tgfx->timer);
tgfx->timer.data = (long) tgfx;
tgfx->timer.function = tgfx_timer;
@@ -198,14 +209,12 @@ static struct tgfx __init *tgfx_probe(int parport, int *n_buttons, int n_devs)
if (n_buttons[i] > ARRAY_SIZE(tgfx_buttons)) {
printk(KERN_ERR "turbografx.c: Invalid number of buttons %d\n", n_buttons[i]);
- err = -EINVAL;
goto err_unreg_devs;
}
tgfx->dev[i] = input_dev = input_allocate_device();
if (!input_dev) {
printk(KERN_ERR "turbografx.c: Not enough memory for input device\n");
- err = -ENOMEM;
goto err_unreg_devs;
}
@@ -234,19 +243,17 @@ static struct tgfx __init *tgfx_probe(int parport, int *n_buttons, int n_devs)
for (j = 0; j < n_buttons[i]; j++)
set_bit(tgfx_buttons[j], input_dev->keybit);
- err = input_register_device(tgfx->dev[i]);
- if (err)
+ if (input_register_device(tgfx->dev[i]))
goto err_free_dev;
}
if (!tgfx->sticks) {
printk(KERN_ERR "turbografx.c: No valid devices specified\n");
- err = -EINVAL;
goto err_free_tgfx;
}
- parport_put_port(pp);
- return tgfx;
+ tgfx_base[port_idx] = tgfx;
+ return;
err_free_dev:
input_free_device(tgfx->dev[i]);
@@ -258,15 +265,23 @@ static struct tgfx __init *tgfx_probe(int parport, int *n_buttons, int n_devs)
kfree(tgfx);
err_unreg_pardev:
parport_unregister_device(pd);
- err_put_pp:
- parport_put_port(pp);
- err_out:
- return ERR_PTR(err);
}
-static void tgfx_remove(struct tgfx *tgfx)
+static void tgfx_detach(struct parport *port)
{
int i;
+ struct tgfx *tgfx;
+
+ for (i = 0; i < TGFX_MAX_PORTS; i++) {
+ if (tgfx_base[i] && tgfx_base[i]->parportno == port->number)
+ break;
+ }
+
+ if (i == TGFX_MAX_PORTS)
+ return;
+
+ tgfx = tgfx_base[i];
+ tgfx_base[i] = NULL;
for (i = 0; i < TGFX_MAX_DEVICES; i++)
if (tgfx->dev[i])
@@ -275,11 +290,17 @@ static void tgfx_remove(struct tgfx *tgfx)
kfree(tgfx);
}
+static struct parport_driver tgfx_parport_driver = {
+ .name = "turbografx",
+ .match_port = tgfx_attach,
+ .detach = tgfx_detach,
+ .devmodel = true,
+};
+
static int __init tgfx_init(void)
{
int i;
int have_dev = 0;
- int err = 0;
for (i = 0; i < TGFX_MAX_PORTS; i++) {
if (tgfx_cfg[i].nargs == 0 || tgfx_cfg[i].args[0] < 0)
@@ -287,38 +308,21 @@ static int __init tgfx_init(void)
if (tgfx_cfg[i].nargs < 2) {
printk(KERN_ERR "turbografx.c: at least one joystick must be specified\n");
- err = -EINVAL;
- break;
- }
-
- tgfx_base[i] = tgfx_probe(tgfx_cfg[i].args[0],
- tgfx_cfg[i].args + 1,
- tgfx_cfg[i].nargs - 1);
- if (IS_ERR(tgfx_base[i])) {
- err = PTR_ERR(tgfx_base[i]);
- break;
+ return -EINVAL;
}
have_dev = 1;
}
- if (err) {
- while (--i >= 0)
- if (tgfx_base[i])
- tgfx_remove(tgfx_base[i]);
- return err;
- }
+ if (!have_dev)
+ return -ENODEV;
- return have_dev ? 0 : -ENODEV;
+ return parport_register_driver(&tgfx_parport_driver);
}
static void __exit tgfx_exit(void)
{
- int i;
-
- for (i = 0; i < TGFX_MAX_PORTS; i++)
- if (tgfx_base[i])
- tgfx_remove(tgfx_base[i]);
+ parport_unregister_driver(&tgfx_parport_driver);
}
module_init(tgfx_init);
diff --git a/drivers/input/joystick/walkera0701.c b/drivers/input/joystick/walkera0701.c
index a8bc2fe170dd..d88f5dd3c9d9 100644
--- a/drivers/input/joystick/walkera0701.c
+++ b/drivers/input/joystick/walkera0701.c
@@ -200,35 +200,38 @@ static void walkera0701_close(struct input_dev *dev)
parport_release(w->pardevice);
}
-static int walkera0701_connect(struct walkera_dev *w, int parport)
+static void walkera0701_attach(struct parport *pp)
{
- int error;
+ struct pardev_cb walkera0701_parport_cb;
+ struct walkera_dev *w = &w_dev;
- w->parport = parport_find_number(parport);
- if (!w->parport) {
- pr_err("parport %d does not exist\n", parport);
- return -ENODEV;
+ if (pp->number != walkera0701_pp_no) {
+ pr_debug("Not using parport%d.\n", pp->number);
+ return;
}
- if (w->parport->irq == -1) {
+ if (pp->irq == -1) {
pr_err("parport %d does not have interrupt assigned\n",
- parport);
- error = -EINVAL;
- goto err_put_parport;
+ pp->number);
+ return;
}
- w->pardevice = parport_register_device(w->parport, "walkera0701",
- NULL, NULL, walkera0701_irq_handler,
- PARPORT_DEV_EXCL, w);
+ w->parport = pp;
+
+ walkera0701_parport_cb.flags = PARPORT_FLAG_EXCL;
+ walkera0701_parport_cb.irq_func = walkera0701_irq_handler;
+ walkera0701_parport_cb.private = w;
+
+ w->pardevice = parport_register_dev_model(pp, "walkera0701",
+ &walkera0701_parport_cb, 0);
+
if (!w->pardevice) {
pr_err("failed to register parport device\n");
- error = -EIO;
- goto err_put_parport;
+ return;
}
if (parport_negotiate(w->pardevice->port, IEEE1284_MODE_COMPAT)) {
pr_err("failed to negotiate parport mode\n");
- error = -EIO;
goto err_unregister_device;
}
@@ -238,7 +241,6 @@ static int walkera0701_connect(struct walkera_dev *w, int parport)
w->input_dev = input_allocate_device();
if (!w->input_dev) {
pr_err("failed to allocate input device\n");
- error = -ENOMEM;
goto err_unregister_device;
}
@@ -265,38 +267,46 @@ static int walkera0701_connect(struct walkera_dev *w, int parport)
input_set_abs_params(w->input_dev, ABS_RUDDER, -512, 512, 0, 0);
input_set_abs_params(w->input_dev, ABS_MISC, -512, 512, 0, 0);
- error = input_register_device(w->input_dev);
- if (error) {
+ if (input_register_device(w->input_dev)) {
pr_err("failed to register input device\n");
goto err_free_input_dev;
}
- return 0;
+ return;
err_free_input_dev:
input_free_device(w->input_dev);
err_unregister_device:
parport_unregister_device(w->pardevice);
-err_put_parport:
- parport_put_port(w->parport);
- return error;
}
-static void walkera0701_disconnect(struct walkera_dev *w)
+static void walkera0701_detach(struct parport *port)
{
+ struct walkera_dev *w = &w_dev;
+
+ if (!w->pardevice || w->parport->number != port->number)
+ return;
+
input_unregister_device(w->input_dev);
parport_unregister_device(w->pardevice);
- parport_put_port(w->parport);
+ w->parport = NULL;
}
+static struct parport_driver walkera0701_parport_driver = {
+ .name = "walkera0701",
+ .match_port = walkera0701_attach,
+ .detach = walkera0701_detach,
+ .devmodel = true,
+};
+
static int __init walkera0701_init(void)
{
- return walkera0701_connect(&w_dev, walkera0701_pp_no);
+ return parport_register_driver(&walkera0701_parport_driver);
}
static void __exit walkera0701_exit(void)
{
- walkera0701_disconnect(&w_dev);
+ parport_unregister_driver(&walkera0701_parport_driver);
}
module_init(walkera0701_init);
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index f8850f9cb331..fd4100d56d8c 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -125,6 +125,7 @@ static const struct xpad_device {
{ 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", 0, XTYPE_XBOX },
{ 0x045e, 0x028e, "Microsoft X-Box 360 pad", 0, XTYPE_XBOX360 },
{ 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE },
+ { 0x045e, 0x02dd, "Microsoft X-Box One pad (Covert Forces)", 0, XTYPE_XBOXONE },
{ 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
{ 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
{ 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX },
@@ -204,7 +205,7 @@ static const struct xpad_device {
{ 0x1bad, 0xf900, "Harmonix Xbox 360 Controller", 0, XTYPE_XBOX360 },
{ 0x1bad, 0xf901, "Gamestop Xbox 360 Controller", 0, XTYPE_XBOX360 },
{ 0x1bad, 0xf903, "Tron Xbox 360 controller", 0, XTYPE_XBOX360 },
- { 0x24c6, 0x5000, "Razer Atrox Arcade Stick", 0, XTYPE_XBOX360 },
+ { 0x24c6, 0x5000, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x24c6, 0x5300, "PowerA MINI PROEX Controller", 0, XTYPE_XBOX360 },
{ 0x24c6, 0x5303, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 },
{ 0x24c6, 0x5500, "Hori XBOX 360 EX 2 with Turbo", 0, XTYPE_XBOX360 },
@@ -242,7 +243,6 @@ static const signed short xpad_btn_triggers[] = {
-1
};
-
static const signed short xpad360_btn[] = { /* buttons for x360 controller */
BTN_TL, BTN_TR, /* Button LB/RB */
BTN_MODE, /* The big X button */
@@ -328,9 +328,6 @@ struct usb_xpad {
unsigned char *idata; /* input data */
dma_addr_t idata_dma;
- struct urb *bulk_out;
- unsigned char *bdata;
-
struct urb *irq_out; /* urb for interrupt out report */
unsigned char *odata; /* output data */
dma_addr_t odata_dma;
@@ -344,7 +341,8 @@ struct usb_xpad {
int mapping; /* map d-pad to buttons or to axes */
int xtype; /* type of xbox device */
- unsigned long led_no; /* led to lit on xbox360 controllers */
+ int pad_nr; /* the order x360 pads were attached */
+ const char *name; /* name of the device */
};
/*
@@ -356,7 +354,6 @@ struct usb_xpad {
* The used report descriptor was taken from ITO Takayukis website:
* http://euc.jp/periphs/xbox-controller.ja.html
*/
-
static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
{
struct input_dev *dev = xpad->dev;
@@ -439,7 +436,16 @@ static void xpad360_process_packet(struct usb_xpad *xpad,
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08);
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01);
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02);
- } else {
+ }
+
+ /*
+ * This should be a simple else block. However historically
+ * xbox360w has mapped DPAD to buttons while xbox360 did not. This
+ * made no sense, but now we can not just switch back and have to
+ * support both behaviors.
+ */
+ if (!(xpad->mapping & MAP_DPAD_TO_BUTTONS) ||
+ xpad->xtype == XTYPE_XBOX360W) {
input_report_abs(dev, ABS_HAT0X,
!!(data[2] & 0x08) - !!(data[2] & 0x04));
input_report_abs(dev, ABS_HAT0Y,
@@ -505,14 +511,12 @@ static void xpad_identify_controller(struct usb_xpad *xpad);
* 01.1 - Pad state (Bytes 4+) valid
*
*/
-
static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
{
/* Presence change */
if (data[0] & 0x08) {
if (data[1] & 0x80) {
xpad->pad_present = 1;
- usb_submit_urb(xpad->bulk_out, GFP_ATOMIC);
/*
* Light up the segment corresponding to
* controller number.
@@ -674,28 +678,6 @@ exit:
__func__, retval);
}
-static void xpad_bulk_out(struct urb *urb)
-{
- struct usb_xpad *xpad = urb->context;
- struct device *dev = &xpad->intf->dev;
-
- switch (urb->status) {
- case 0:
- /* success */
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /* this urb is terminated, clean up */
- dev_dbg(dev, "%s - urb shutting down with status: %d\n",
- __func__, urb->status);
- break;
- default:
- dev_dbg(dev, "%s - nonzero urb status received: %d\n",
- __func__, urb->status);
- }
-}
-
static void xpad_irq_out(struct urb *urb)
{
struct usb_xpad *xpad = urb->context;
@@ -786,84 +768,109 @@ static void xpad_deinit_output(struct usb_xpad *xpad)
}
}
+static int xpad_inquiry_pad_presence(struct usb_xpad *xpad)
+{
+ int retval;
+
+ mutex_lock(&xpad->odata_mutex);
+
+ xpad->odata[0] = 0x08;
+ xpad->odata[1] = 0x00;
+ xpad->odata[2] = 0x0F;
+ xpad->odata[3] = 0xC0;
+ xpad->odata[4] = 0x00;
+ xpad->odata[5] = 0x00;
+ xpad->odata[6] = 0x00;
+ xpad->odata[7] = 0x00;
+ xpad->odata[8] = 0x00;
+ xpad->odata[9] = 0x00;
+ xpad->odata[10] = 0x00;
+ xpad->odata[11] = 0x00;
+ xpad->irq_out->transfer_buffer_length = 12;
+
+ retval = usb_submit_urb(xpad->irq_out, GFP_KERNEL);
+
+ mutex_unlock(&xpad->odata_mutex);
+
+ return retval;
+}
+
#ifdef CONFIG_JOYSTICK_XPAD_FF
static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect)
{
struct usb_xpad *xpad = input_get_drvdata(dev);
+ __u16 strong;
+ __u16 weak;
- if (effect->type == FF_RUMBLE) {
- __u16 strong = effect->u.rumble.strong_magnitude;
- __u16 weak = effect->u.rumble.weak_magnitude;
-
- switch (xpad->xtype) {
-
- case XTYPE_XBOX:
- xpad->odata[0] = 0x00;
- xpad->odata[1] = 0x06;
- xpad->odata[2] = 0x00;
- xpad->odata[3] = strong / 256; /* left actuator */
- xpad->odata[4] = 0x00;
- xpad->odata[5] = weak / 256; /* right actuator */
- xpad->irq_out->transfer_buffer_length = 6;
-
- return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
-
- case XTYPE_XBOX360:
- xpad->odata[0] = 0x00;
- xpad->odata[1] = 0x08;
- xpad->odata[2] = 0x00;
- xpad->odata[3] = strong / 256; /* left actuator? */
- xpad->odata[4] = weak / 256; /* right actuator? */
- xpad->odata[5] = 0x00;
- xpad->odata[6] = 0x00;
- xpad->odata[7] = 0x00;
- xpad->irq_out->transfer_buffer_length = 8;
-
- return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
-
- case XTYPE_XBOX360W:
- xpad->odata[0] = 0x00;
- xpad->odata[1] = 0x01;
- xpad->odata[2] = 0x0F;
- xpad->odata[3] = 0xC0;
- xpad->odata[4] = 0x00;
- xpad->odata[5] = strong / 256;
- xpad->odata[6] = weak / 256;
- xpad->odata[7] = 0x00;
- xpad->odata[8] = 0x00;
- xpad->odata[9] = 0x00;
- xpad->odata[10] = 0x00;
- xpad->odata[11] = 0x00;
- xpad->irq_out->transfer_buffer_length = 12;
-
- return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
-
- case XTYPE_XBOXONE:
- xpad->odata[0] = 0x09; /* activate rumble */
- xpad->odata[1] = 0x08;
- xpad->odata[2] = 0x00;
- xpad->odata[3] = 0x08; /* continuous effect */
- xpad->odata[4] = 0x00; /* simple rumble mode */
- xpad->odata[5] = 0x03; /* L and R actuator only */
- xpad->odata[6] = 0x00; /* TODO: LT actuator */
- xpad->odata[7] = 0x00; /* TODO: RT actuator */
- xpad->odata[8] = strong / 256; /* left actuator */
- xpad->odata[9] = weak / 256; /* right actuator */
- xpad->odata[10] = 0x80; /* length of pulse */
- xpad->odata[11] = 0x00; /* stop period of pulse */
- xpad->irq_out->transfer_buffer_length = 12;
-
- return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
-
- default:
- dev_dbg(&xpad->dev->dev,
- "%s - rumble command sent to unsupported xpad type: %d\n",
- __func__, xpad->xtype);
- return -1;
- }
+ if (effect->type != FF_RUMBLE)
+ return 0;
+
+ strong = effect->u.rumble.strong_magnitude;
+ weak = effect->u.rumble.weak_magnitude;
+
+ switch (xpad->xtype) {
+ case XTYPE_XBOX:
+ xpad->odata[0] = 0x00;
+ xpad->odata[1] = 0x06;
+ xpad->odata[2] = 0x00;
+ xpad->odata[3] = strong / 256; /* left actuator */
+ xpad->odata[4] = 0x00;
+ xpad->odata[5] = weak / 256; /* right actuator */
+ xpad->irq_out->transfer_buffer_length = 6;
+ break;
+
+ case XTYPE_XBOX360:
+ xpad->odata[0] = 0x00;
+ xpad->odata[1] = 0x08;
+ xpad->odata[2] = 0x00;
+ xpad->odata[3] = strong / 256; /* left actuator? */
+ xpad->odata[4] = weak / 256; /* right actuator? */
+ xpad->odata[5] = 0x00;
+ xpad->odata[6] = 0x00;
+ xpad->odata[7] = 0x00;
+ xpad->irq_out->transfer_buffer_length = 8;
+ break;
+
+ case XTYPE_XBOX360W:
+ xpad->odata[0] = 0x00;
+ xpad->odata[1] = 0x01;
+ xpad->odata[2] = 0x0F;
+ xpad->odata[3] = 0xC0;
+ xpad->odata[4] = 0x00;
+ xpad->odata[5] = strong / 256;
+ xpad->odata[6] = weak / 256;
+ xpad->odata[7] = 0x00;
+ xpad->odata[8] = 0x00;
+ xpad->odata[9] = 0x00;
+ xpad->odata[10] = 0x00;
+ xpad->odata[11] = 0x00;
+ xpad->irq_out->transfer_buffer_length = 12;
+ break;
+
+ case XTYPE_XBOXONE:
+ xpad->odata[0] = 0x09; /* activate rumble */
+ xpad->odata[1] = 0x08;
+ xpad->odata[2] = 0x00;
+ xpad->odata[3] = 0x08; /* continuous effect */
+ xpad->odata[4] = 0x00; /* simple rumble mode */
+ xpad->odata[5] = 0x03; /* L and R actuator only */
+ xpad->odata[6] = 0x00; /* TODO: LT actuator */
+ xpad->odata[7] = 0x00; /* TODO: RT actuator */
+ xpad->odata[8] = strong / 256; /* left actuator */
+ xpad->odata[9] = weak / 256; /* right actuator */
+ xpad->odata[10] = 0x80; /* length of pulse */
+ xpad->odata[11] = 0x00; /* stop period of pulse */
+ xpad->irq_out->transfer_buffer_length = 12;
+ break;
+
+ default:
+ dev_dbg(&xpad->dev->dev,
+ "%s - rumble command sent to unsupported xpad type: %d\n",
+ __func__, xpad->xtype);
+ return -EINVAL;
}
- return 0;
+ return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
}
static int xpad_init_ff(struct usb_xpad *xpad)
@@ -882,6 +889,9 @@ static int xpad_init_ff(struct usb_xpad *xpad) { return 0; }
#if defined(CONFIG_JOYSTICK_XPAD_LEDS)
#include <linux/leds.h>
+#include <linux/idr.h>
+
+static DEFINE_IDA(xpad_pad_seq);
struct xpad_led {
char name[16];
@@ -890,6 +900,7 @@ struct xpad_led {
};
/**
+ * set the LEDs on Xbox360 / Wireless Controllers
* @param command
* 0: off
* 1: all blink, then previous setting
@@ -942,10 +953,13 @@ static void xpad_send_led_command(struct usb_xpad *xpad, int command)
mutex_unlock(&xpad->odata_mutex);
}
+/*
+ * Light up the segment corresponding to the pad number on
+ * Xbox 360 Controllers.
+ */
static void xpad_identify_controller(struct usb_xpad *xpad)
{
- /* Light up the segment corresponding to controller number */
- xpad_send_led_command(xpad, (xpad->led_no % 4) + 2);
+ xpad_send_led_command(xpad, (xpad->pad_nr % 4) + 2);
}
static void xpad_led_set(struct led_classdev *led_cdev,
@@ -959,7 +973,6 @@ static void xpad_led_set(struct led_classdev *led_cdev,
static int xpad_led_probe(struct usb_xpad *xpad)
{
- static atomic_t led_seq = ATOMIC_INIT(-1);
struct xpad_led *led;
struct led_classdev *led_cdev;
int error;
@@ -971,9 +984,13 @@ static int xpad_led_probe(struct usb_xpad *xpad)
if (!led)
return -ENOMEM;
- xpad->led_no = atomic_inc_return(&led_seq);
+ xpad->pad_nr = ida_simple_get(&xpad_pad_seq, 0, 0, GFP_KERNEL);
+ if (xpad->pad_nr < 0) {
+ error = xpad->pad_nr;
+ goto err_free_mem;
+ }
- snprintf(led->name, sizeof(led->name), "xpad%lu", xpad->led_no);
+ snprintf(led->name, sizeof(led->name), "xpad%d", xpad->pad_nr);
led->xpad = xpad;
led_cdev = &led->led_cdev;
@@ -981,16 +998,26 @@ static int xpad_led_probe(struct usb_xpad *xpad)
led_cdev->brightness_set = xpad_led_set;
error = led_classdev_register(&xpad->udev->dev, led_cdev);
- if (error) {
- kfree(led);
- xpad->led = NULL;
- return error;
- }
+ if (error)
+ goto err_free_id;
- /* Light up the segment corresponding to controller number */
- xpad_identify_controller(xpad);
+ if (xpad->xtype == XTYPE_XBOX360) {
+ /*
+ * Light up the segment corresponding to controller
+ * number on wired devices. On wireless we'll do that
+ * when they respond to "presence" packet.
+ */
+ xpad_identify_controller(xpad);
+ }
return 0;
+
+err_free_id:
+ ida_simple_remove(&xpad_pad_seq, xpad->pad_nr);
+err_free_mem:
+ kfree(led);
+ xpad->led = NULL;
+ return error;
}
static void xpad_led_disconnect(struct usb_xpad *xpad)
@@ -999,6 +1026,7 @@ static void xpad_led_disconnect(struct usb_xpad *xpad)
if (xpad_led) {
led_classdev_unregister(&xpad_led->led_cdev);
+ ida_simple_remove(&xpad_pad_seq, xpad->pad_nr);
kfree(xpad_led);
}
}
@@ -1008,7 +1036,6 @@ static void xpad_led_disconnect(struct usb_xpad *xpad) { }
static void xpad_identify_controller(struct usb_xpad *xpad) { }
#endif
-
static int xpad_open(struct input_dev *dev)
{
struct usb_xpad *xpad = input_get_drvdata(dev);
@@ -1068,11 +1095,107 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
}
}
+static void xpad_deinit_input(struct usb_xpad *xpad)
+{
+ xpad_led_disconnect(xpad);
+ input_unregister_device(xpad->dev);
+}
+
+static int xpad_init_input(struct usb_xpad *xpad)
+{
+ struct input_dev *input_dev;
+ int i, error;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+
+ xpad->dev = input_dev;
+ input_dev->name = xpad->name;
+ input_dev->phys = xpad->phys;
+ usb_to_input_id(xpad->udev, &input_dev->id);
+ input_dev->dev.parent = &xpad->intf->dev;
+
+ input_set_drvdata(input_dev, xpad);
+
+ input_dev->open = xpad_open;
+ input_dev->close = xpad_close;
+
+ __set_bit(EV_KEY, input_dev->evbit);
+
+ if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
+ __set_bit(EV_ABS, input_dev->evbit);
+ /* set up axes */
+ for (i = 0; xpad_abs[i] >= 0; i++)
+ xpad_set_up_abs(input_dev, xpad_abs[i]);
+ }
+
+ /* set up standard buttons */
+ for (i = 0; xpad_common_btn[i] >= 0; i++)
+ __set_bit(xpad_common_btn[i], input_dev->keybit);
+
+ /* set up model-specific ones */
+ if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W ||
+ xpad->xtype == XTYPE_XBOXONE) {
+ for (i = 0; xpad360_btn[i] >= 0; i++)
+ __set_bit(xpad360_btn[i], input_dev->keybit);
+ } else {
+ for (i = 0; xpad_btn[i] >= 0; i++)
+ __set_bit(xpad_btn[i], input_dev->keybit);
+ }
+
+ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
+ for (i = 0; xpad_btn_pad[i] >= 0; i++)
+ __set_bit(xpad_btn_pad[i], input_dev->keybit);
+ }
+
+ /*
+ * This should be a simple else block. However historically
+ * xbox360w has mapped DPAD to buttons while xbox360 did not. This
+ * made no sense, but now we can not just switch back and have to
+ * support both behaviors.
+ */
+ if (!(xpad->mapping & MAP_DPAD_TO_BUTTONS) ||
+ xpad->xtype == XTYPE_XBOX360W) {
+ for (i = 0; xpad_abs_pad[i] >= 0; i++)
+ xpad_set_up_abs(input_dev, xpad_abs_pad[i]);
+ }
+
+ if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
+ for (i = 0; xpad_btn_triggers[i] >= 0; i++)
+ __set_bit(xpad_btn_triggers[i], input_dev->keybit);
+ } else {
+ for (i = 0; xpad_abs_triggers[i] >= 0; i++)
+ xpad_set_up_abs(input_dev, xpad_abs_triggers[i]);
+ }
+
+ error = xpad_init_ff(xpad);
+ if (error)
+ goto err_free_input;
+
+ error = xpad_led_probe(xpad);
+ if (error)
+ goto err_destroy_ff;
+
+ error = input_register_device(xpad->dev);
+ if (error)
+ goto err_disconnect_led;
+
+ return 0;
+
+err_disconnect_led:
+ xpad_led_disconnect(xpad);
+err_destroy_ff:
+ input_ff_destroy(input_dev);
+err_free_input:
+ input_free_device(input_dev);
+ return error;
+}
+
static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_xpad *xpad;
- struct input_dev *input_dev;
struct usb_endpoint_descriptor *ep_irq_in;
int ep_irq_in_idx;
int i, error;
@@ -1094,29 +1217,30 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
}
xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!xpad || !input_dev) {
- error = -ENOMEM;
- goto fail1;
- }
+ if (!xpad)
+ return -ENOMEM;
+
+ usb_make_path(udev, xpad->phys, sizeof(xpad->phys));
+ strlcat(xpad->phys, "/input0", sizeof(xpad->phys));
xpad->idata = usb_alloc_coherent(udev, XPAD_PKT_LEN,
GFP_KERNEL, &xpad->idata_dma);
if (!xpad->idata) {
error = -ENOMEM;
- goto fail1;
+ goto err_free_mem;
}
xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL);
if (!xpad->irq_in) {
error = -ENOMEM;
- goto fail2;
+ goto err_free_idata;
}
xpad->udev = udev;
xpad->intf = intf;
xpad->mapping = xpad_device[i].mapping;
xpad->xtype = xpad_device[i].xtype;
+ xpad->name = xpad_device[i].name;
if (xpad->xtype == XTYPE_UNKNOWN) {
if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
@@ -1124,8 +1248,9 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
xpad->xtype = XTYPE_XBOX360W;
else
xpad->xtype = XTYPE_XBOX360;
- } else
+ } else {
xpad->xtype = XTYPE_XBOX;
+ }
if (dpad_to_buttons)
xpad->mapping |= MAP_DPAD_TO_BUTTONS;
@@ -1135,70 +1260,9 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
xpad->mapping |= MAP_STICKS_TO_NULL;
}
- xpad->dev = input_dev;
- usb_make_path(udev, xpad->phys, sizeof(xpad->phys));
- strlcat(xpad->phys, "/input0", sizeof(xpad->phys));
-
- input_dev->name = xpad_device[i].name;
- input_dev->phys = xpad->phys;
- usb_to_input_id(udev, &input_dev->id);
- input_dev->dev.parent = &intf->dev;
-
- input_set_drvdata(input_dev, xpad);
-
- input_dev->open = xpad_open;
- input_dev->close = xpad_close;
-
- input_dev->evbit[0] = BIT_MASK(EV_KEY);
-
- if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
- input_dev->evbit[0] |= BIT_MASK(EV_ABS);
- /* set up axes */
- for (i = 0; xpad_abs[i] >= 0; i++)
- xpad_set_up_abs(input_dev, xpad_abs[i]);
- }
-
- /* set up standard buttons */
- for (i = 0; xpad_common_btn[i] >= 0; i++)
- __set_bit(xpad_common_btn[i], input_dev->keybit);
-
- /* set up model-specific ones */
- if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W ||
- xpad->xtype == XTYPE_XBOXONE) {
- for (i = 0; xpad360_btn[i] >= 0; i++)
- __set_bit(xpad360_btn[i], input_dev->keybit);
- } else {
- for (i = 0; xpad_btn[i] >= 0; i++)
- __set_bit(xpad_btn[i], input_dev->keybit);
- }
-
- if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
- for (i = 0; xpad_btn_pad[i] >= 0; i++)
- __set_bit(xpad_btn_pad[i], input_dev->keybit);
- } else {
- for (i = 0; xpad_abs_pad[i] >= 0; i++)
- xpad_set_up_abs(input_dev, xpad_abs_pad[i]);
- }
-
- if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
- for (i = 0; xpad_btn_triggers[i] >= 0; i++)
- __set_bit(xpad_btn_triggers[i], input_dev->keybit);
- } else {
- for (i = 0; xpad_abs_triggers[i] >= 0; i++)
- xpad_set_up_abs(input_dev, xpad_abs_triggers[i]);
- }
-
error = xpad_init_output(intf, xpad);
if (error)
- goto fail3;
-
- error = xpad_init_ff(xpad);
- if (error)
- goto fail4;
-
- error = xpad_led_probe(xpad);
- if (error)
- goto fail5;
+ goto err_free_in_urb;
/* Xbox One controller has in/out endpoints swapped. */
ep_irq_in_idx = xpad->xtype == XTYPE_XBOXONE ? 1 : 0;
@@ -1211,59 +1275,13 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
xpad->irq_in->transfer_dma = xpad->idata_dma;
xpad->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- error = input_register_device(xpad->dev);
- if (error)
- goto fail6;
-
usb_set_intfdata(intf, xpad);
- if (xpad->xtype == XTYPE_XBOX360W) {
- /*
- * Setup the message to set the LEDs on the
- * controller when it shows up
- */
- xpad->bulk_out = usb_alloc_urb(0, GFP_KERNEL);
- if (!xpad->bulk_out) {
- error = -ENOMEM;
- goto fail7;
- }
-
- xpad->bdata = kzalloc(XPAD_PKT_LEN, GFP_KERNEL);
- if (!xpad->bdata) {
- error = -ENOMEM;
- goto fail8;
- }
-
- xpad->bdata[2] = 0x08;
- switch (intf->cur_altsetting->desc.bInterfaceNumber) {
- case 0:
- xpad->bdata[3] = 0x42;
- break;
- case 2:
- xpad->bdata[3] = 0x43;
- break;
- case 4:
- xpad->bdata[3] = 0x44;
- break;
- case 6:
- xpad->bdata[3] = 0x45;
- }
-
- ep_irq_in = &intf->cur_altsetting->endpoint[1].desc;
- if (usb_endpoint_is_bulk_out(ep_irq_in)) {
- usb_fill_bulk_urb(xpad->bulk_out, udev,
- usb_sndbulkpipe(udev,
- ep_irq_in->bEndpointAddress),
- xpad->bdata, XPAD_PKT_LEN,
- xpad_bulk_out, xpad);
- } else {
- usb_fill_int_urb(xpad->bulk_out, udev,
- usb_sndintpipe(udev,
- ep_irq_in->bEndpointAddress),
- xpad->bdata, XPAD_PKT_LEN,
- xpad_bulk_out, xpad, 0);
- }
+ error = xpad_init_input(xpad);
+ if (error)
+ goto err_deinit_output;
+ if (xpad->xtype == XTYPE_XBOX360W) {
/*
* Submit the int URB immediately rather than waiting for open
* because we get status messages from the device whether
@@ -1274,22 +1292,32 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
xpad->irq_in->dev = xpad->udev;
error = usb_submit_urb(xpad->irq_in, GFP_KERNEL);
if (error)
- goto fail9;
- }
+ goto err_deinit_input;
+ /*
+ * Send presence packet.
+ * This will force the controller to resend connection packets.
+ * This is useful in the case we activate the module after the
+ * adapter has been plugged in, as it won't automatically
+ * send us info about the controllers.
+ */
+ error = xpad_inquiry_pad_presence(xpad);
+ if (error)
+ goto err_kill_in_urb;
+ }
return 0;
- fail9: kfree(xpad->bdata);
- fail8: usb_free_urb(xpad->bulk_out);
- fail7: input_unregister_device(input_dev);
- input_dev = NULL;
- fail6: xpad_led_disconnect(xpad);
- fail5: if (input_dev)
- input_ff_destroy(input_dev);
- fail4: xpad_deinit_output(xpad);
- fail3: usb_free_urb(xpad->irq_in);
- fail2: usb_free_coherent(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
- fail1: input_free_device(input_dev);
+err_kill_in_urb:
+ usb_kill_urb(xpad->irq_in);
+err_deinit_input:
+ xpad_deinit_input(xpad);
+err_deinit_output:
+ xpad_deinit_output(xpad);
+err_free_in_urb:
+ usb_free_urb(xpad->irq_in);
+err_free_idata:
+ usb_free_coherent(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
+err_free_mem:
kfree(xpad);
return error;
@@ -1299,13 +1327,10 @@ static void xpad_disconnect(struct usb_interface *intf)
{
struct usb_xpad *xpad = usb_get_intfdata (intf);
- xpad_led_disconnect(xpad);
- input_unregister_device(xpad->dev);
+ xpad_deinit_input(xpad);
xpad_deinit_output(xpad);
if (xpad->xtype == XTYPE_XBOX360W) {
- usb_kill_urb(xpad->bulk_out);
- usb_free_urb(xpad->bulk_out);
usb_kill_urb(xpad->irq_in);
}
@@ -1313,7 +1338,6 @@ static void xpad_disconnect(struct usb_interface *intf)
usb_free_coherent(xpad->udev, XPAD_PKT_LEN,
xpad->idata, xpad->idata_dma);
- kfree(xpad->bdata);
kfree(xpad);
usb_set_intfdata(intf, NULL);
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 398d4be8159e..ddd8148d51d7 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -401,6 +401,17 @@ config KEYBOARD_MPR121
To compile this driver as a module, choose M here: the
module will be called mpr121_touchkey.
+config KEYBOARD_SNVS_PWRKEY
+ tristate "IMX SNVS Power Key Driver"
+ depends on SOC_IMX6SX
+ depends on OF
+ help
+ This is the snvs powerkey driver for the Freescale i.MX application
+ processors that are newer than i.MX6 SX.
+
+ To compile this driver as a module, choose M here; the
+ module will be called snvs_pwrkey.
+
config KEYBOARD_IMX
tristate "IMX keypad support"
depends on ARCH_MXC
@@ -505,7 +516,7 @@ config KEYBOARD_SAMSUNG
module will be called samsung-keypad.
config KEYBOARD_GOLDFISH_EVENTS
- depends on GOLDFISH
+ depends on GOLDFISH || COMPILE_TEST
tristate "Generic Input Event device for Goldfish"
help
Say Y here to get an input event device for the Goldfish virtual
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index df28d5553c05..1d416ddf84e4 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_KEYBOARD_QT1070) += qt1070.o
obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o
obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o
obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
+obj-$(CONFIG_KEYBOARD_SNVS_PWRKEY) += snvs_pwrkey.o
obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o
obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index 9d517ca7eb5a..bef317ff7352 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -341,8 +341,14 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
const struct gpio_keys_button *button = bdata->button;
struct input_dev *input = bdata->input;
unsigned int type = button->type ?: EV_KEY;
- int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low;
+ int state = gpio_get_value_cansleep(button->gpio);
+ if (state < 0) {
+ dev_err(input->dev.parent, "failed to get gpio state\n");
+ return;
+ }
+
+ state = (state ? 1 : 0) ^ button->active_low;
if (type == EV_ABS) {
if (state)
input_event(input, type, button->code, button->value);
diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c
index 870cfa6e2c44..62bdb1d48c49 100644
--- a/drivers/input/keyboard/gpio_keys_polled.c
+++ b/drivers/input/keyboard/gpio_keys_polled.c
@@ -40,10 +40,36 @@ struct gpio_keys_polled_dev {
struct input_polled_dev *poll_dev;
struct device *dev;
const struct gpio_keys_platform_data *pdata;
+ unsigned long rel_axis_seen[BITS_TO_LONGS(REL_CNT)];
+ unsigned long abs_axis_seen[BITS_TO_LONGS(ABS_CNT)];
struct gpio_keys_button_data data[0];
};
-static void gpio_keys_polled_check_state(struct input_dev *input,
+static void gpio_keys_button_event(struct input_polled_dev *dev,
+ struct gpio_keys_button *button,
+ int state)
+{
+ struct gpio_keys_polled_dev *bdev = dev->private;
+ struct input_dev *input = dev->input;
+ unsigned int type = button->type ?: EV_KEY;
+
+ if (type == EV_REL) {
+ if (state) {
+ input_event(input, type, button->code, button->value);
+ __set_bit(button->code, bdev->rel_axis_seen);
+ }
+ } else if (type == EV_ABS) {
+ if (state) {
+ input_event(input, type, button->code, button->value);
+ __set_bit(button->code, bdev->abs_axis_seen);
+ }
+ } else {
+ input_event(input, type, button->code, state);
+ input_sync(input);
+ }
+}
+
+static void gpio_keys_polled_check_state(struct input_polled_dev *dev,
struct gpio_keys_button *button,
struct gpio_keys_button_data *bdata)
{
@@ -54,11 +80,9 @@ static void gpio_keys_polled_check_state(struct input_dev *input,
else
state = !!gpiod_get_value(button->gpiod);
- if (state != bdata->last_state) {
- unsigned int type = button->type ?: EV_KEY;
+ gpio_keys_button_event(dev, button, state);
- input_event(input, type, button->code, state);
- input_sync(input);
+ if (state != bdata->last_state) {
bdata->count = 0;
bdata->last_state = state;
}
@@ -71,15 +95,33 @@ static void gpio_keys_polled_poll(struct input_polled_dev *dev)
struct input_dev *input = dev->input;
int i;
+ memset(bdev->rel_axis_seen, 0, sizeof(bdev->rel_axis_seen));
+ memset(bdev->abs_axis_seen, 0, sizeof(bdev->abs_axis_seen));
+
for (i = 0; i < pdata->nbuttons; i++) {
struct gpio_keys_button_data *bdata = &bdev->data[i];
- if (bdata->count < bdata->threshold)
+ if (bdata->count < bdata->threshold) {
bdata->count++;
- else
- gpio_keys_polled_check_state(input, &pdata->buttons[i],
+ gpio_keys_button_event(dev, &pdata->buttons[i],
+ bdata->last_state);
+ } else {
+ gpio_keys_polled_check_state(dev, &pdata->buttons[i],
bdata);
+ }
+ }
+
+ for_each_set_bit(i, input->relbit, REL_CNT) {
+ if (!test_bit(i, bdev->rel_axis_seen))
+ input_event(input, EV_REL, i, 0);
+ }
+
+ for_each_set_bit(i, input->absbit, ABS_CNT) {
+ if (!test_bit(i, bdev->abs_axis_seen))
+ input_event(input, EV_ABS, i, 0);
}
+
+ input_sync(input);
}
static void gpio_keys_polled_open(struct input_polled_dev *dev)
@@ -152,6 +194,10 @@ static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct
&button->type))
button->type = EV_KEY;
+ if (fwnode_property_read_u32(child, "linux,input-value",
+ (u32 *)&button->value))
+ button->value = 1;
+
button->wakeup =
fwnode_property_read_bool(child, "wakeup-source") ||
/* legacy name */
@@ -168,6 +214,25 @@ static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct
return pdata;
}
+static void gpio_keys_polled_set_abs_params(struct input_dev *input,
+ const struct gpio_keys_platform_data *pdata, unsigned int code)
+{
+ int i, min = 0, max = 0;
+
+ for (i = 0; i < pdata->nbuttons; i++) {
+ struct gpio_keys_button *button = &pdata->buttons[i];
+
+ if (button->type != EV_ABS || button->code != code)
+ continue;
+
+ if (button->value < min)
+ min = button->value;
+ if (button->value > max)
+ max = button->value;
+ }
+ input_set_abs_params(input, code, min, max, 0, 0);
+}
+
static const struct of_device_id gpio_keys_polled_of_match[] = {
{ .compatible = "gpio-keys-polled", },
{ },
@@ -274,6 +339,9 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
pdata->poll_interval);
input_set_capability(input, type, button->code);
+ if (type == EV_ABS)
+ gpio_keys_polled_set_abs_params(input, pdata,
+ button->code);
}
bdev->poll_dev = poll_dev;
@@ -290,9 +358,11 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
/* report initial state of the buttons */
for (i = 0; i < pdata->nbuttons; i++)
- gpio_keys_polled_check_state(input, &pdata->buttons[i],
+ gpio_keys_polled_check_state(poll_dev, &pdata->buttons[i],
&bdev->data[i]);
+ input_sync(input);
+
return 0;
}
diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c
index c7d5b1666fc3..8567ee47761e 100644
--- a/drivers/input/keyboard/nomadik-ske-keypad.c
+++ b/drivers/input/keyboard/nomadik-ske-keypad.c
@@ -54,7 +54,7 @@
/**
* struct ske_keypad - data structure used by keypad driver
* @irq: irq no
- * @reg_base: ske regsiters base address
+ * @reg_base: ske registers base address
* @input: pointer to input device object
* @board: keypad platform device
* @keymap: matrix scan code table for keycodes
diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c
new file mode 100644
index 000000000000..9adf13a5864a
--- /dev/null
+++ b/drivers/input/keyboard/snvs_pwrkey.c
@@ -0,0 +1,226 @@
+/*
+ * Driver for the IMX SNVS ON/OFF Power Key
+ * Copyright (C) 2015 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#define SNVS_LPSR_REG 0x4C /* LP Status Register */
+#define SNVS_LPCR_REG 0x38 /* LP Control Register */
+#define SNVS_HPSR_REG 0x14
+#define SNVS_HPSR_BTN BIT(6)
+#define SNVS_LPSR_SPO BIT(18)
+#define SNVS_LPCR_DEP_EN BIT(5)
+
+#define DEBOUNCE_TIME 30
+#define REPEAT_INTERVAL 60
+
+struct pwrkey_drv_data {
+ struct regmap *snvs;
+ int irq;
+ int keycode;
+ int keystate; /* 1:pressed */
+ int wakeup;
+ struct timer_list check_timer;
+ struct input_dev *input;
+};
+
+static void imx_imx_snvs_check_for_events(unsigned long data)
+{
+ struct pwrkey_drv_data *pdata = (struct pwrkey_drv_data *) data;
+ struct input_dev *input = pdata->input;
+ u32 state;
+
+ regmap_read(pdata->snvs, SNVS_HPSR_REG, &state);
+ state = state & SNVS_HPSR_BTN ? 1 : 0;
+
+ /* only report new event if status changed */
+ if (state ^ pdata->keystate) {
+ pdata->keystate = state;
+ input_event(input, EV_KEY, pdata->keycode, state);
+ input_sync(input);
+ pm_relax(pdata->input->dev.parent);
+ }
+
+ /* repeat check if pressed long */
+ if (state) {
+ mod_timer(&pdata->check_timer,
+ jiffies + msecs_to_jiffies(REPEAT_INTERVAL));
+ }
+}
+
+static irqreturn_t imx_snvs_pwrkey_interrupt(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
+ u32 lp_status;
+
+ pm_wakeup_event(pdata->input->dev.parent, 0);
+
+ regmap_read(pdata->snvs, SNVS_LPSR_REG, &lp_status);
+ if (lp_status & SNVS_LPSR_SPO)
+ mod_timer(&pdata->check_timer, jiffies + msecs_to_jiffies(DEBOUNCE_TIME));
+
+ /* clear SPO status */
+ regmap_write(pdata->snvs, SNVS_LPSR_REG, SNVS_LPSR_SPO);
+
+ return IRQ_HANDLED;
+}
+
+static void imx_snvs_pwrkey_act(void *pdata)
+{
+ struct pwrkey_drv_data *pd = pdata;
+
+ del_timer_sync(&pd->check_timer);
+}
+
+static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
+{
+ struct pwrkey_drv_data *pdata = NULL;
+ struct input_dev *input = NULL;
+ struct device_node *np;
+ int error;
+
+ /* Get SNVS register Page */
+ np = pdev->dev.of_node;
+ if (!np)
+ return -ENODEV;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->snvs = syscon_regmap_lookup_by_phandle(np, "regmap");
+ if (!pdata->snvs) {
+ dev_err(&pdev->dev, "Can't get snvs syscon\n");
+ return -ENODEV;
+ }
+
+ if (of_property_read_u32(np, "linux,keycode", &pdata->keycode)) {
+ pdata->keycode = KEY_POWER;
+ dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n");
+ }
+
+ pdata->wakeup = of_property_read_bool(np, "wakeup-source");
+
+ pdata->irq = platform_get_irq(pdev, 0);
+ if (pdata->irq < 0) {
+ dev_err(&pdev->dev, "no irq defined in platform data\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(pdata->snvs, SNVS_LPCR_REG, SNVS_LPCR_DEP_EN, SNVS_LPCR_DEP_EN);
+
+ /* clear the unexpected interrupt before driver ready */
+ regmap_write(pdata->snvs, SNVS_LPSR_REG, SNVS_LPSR_SPO);
+
+ setup_timer(&pdata->check_timer,
+ imx_imx_snvs_check_for_events, (unsigned long) pdata);
+
+ input = devm_input_allocate_device(&pdev->dev);
+ if (!input) {
+ dev_err(&pdev->dev, "failed to allocate the input device\n");
+ return -ENOMEM;
+ }
+
+ input->name = pdev->name;
+ input->phys = "snvs-pwrkey/input0";
+ input->id.bustype = BUS_HOST;
+
+ input_set_capability(input, EV_KEY, pdata->keycode);
+
+ /* input customer action to cancel release timer */
+ error = devm_add_action(&pdev->dev, imx_snvs_pwrkey_act, pdata);
+ if (error) {
+ dev_err(&pdev->dev, "failed to register remove action\n");
+ return error;
+ }
+
+ error = devm_request_irq(&pdev->dev, pdata->irq,
+ imx_snvs_pwrkey_interrupt,
+ 0, pdev->name, pdev);
+
+ if (error) {
+ dev_err(&pdev->dev, "interrupt not available.\n");
+ return error;
+ }
+
+ error = input_register_device(input);
+ if (error < 0) {
+ dev_err(&pdev->dev, "failed to register input device\n");
+ input_free_device(input);
+ return error;
+ }
+
+ pdata->input = input;
+ platform_set_drvdata(pdev, pdata);
+
+ device_init_wakeup(&pdev->dev, pdata->wakeup);
+
+ return 0;
+}
+
+static int imx_snvs_pwrkey_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(&pdev->dev))
+ enable_irq_wake(pdata->irq);
+
+ return 0;
+}
+
+static int imx_snvs_pwrkey_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(&pdev->dev))
+ disable_irq_wake(pdata->irq);
+
+ return 0;
+}
+
+static const struct of_device_id imx_snvs_pwrkey_ids[] = {
+ { .compatible = "fsl,sec-v4.0-pwrkey" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_snvs_pwrkey_ids);
+
+static SIMPLE_DEV_PM_OPS(imx_snvs_pwrkey_pm_ops, imx_snvs_pwrkey_suspend,
+ imx_snvs_pwrkey_resume);
+
+static struct platform_driver imx_snvs_pwrkey_driver = {
+ .driver = {
+ .name = "snvs_pwrkey",
+ .pm = &imx_snvs_pwrkey_pm_ops,
+ .of_match_table = imx_snvs_pwrkey_ids,
+ },
+ .probe = imx_snvs_pwrkey_probe,
+};
+module_platform_driver(imx_snvs_pwrkey_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("i.MX snvs power key Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c
index f97c73bd14f8..acc5394afb03 100644
--- a/drivers/input/keyboard/tegra-kbc.c
+++ b/drivers/input/keyboard/tegra-kbc.c
@@ -517,7 +517,8 @@ static int tegra_kbc_parse_dt(struct tegra_kbc *kbc)
if (of_find_property(np, "nvidia,needs-ghost-filter", NULL))
kbc->use_ghost_filter = true;
- if (of_find_property(np, "nvidia,wakeup-source", NULL))
+ if (of_property_read_bool(np, "wakeup-source") ||
+ of_property_read_bool(np, "nvidia,wakeup-source")) /* legacy */
kbc->wakeup = true;
if (!of_get_property(np, "nvidia,kbc-row-pins", &proplen)) {
@@ -705,7 +706,7 @@ static int tegra_kbc_probe(struct platform_device *pdev)
input_set_drvdata(kbc->idev, kbc);
err = devm_request_irq(&pdev->dev, kbc->irq, tegra_kbc_isr,
- IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH, pdev->name, kbc);
+ IRQF_TRIGGER_HIGH, pdev->name, kbc);
if (err) {
dev_err(&pdev->dev, "failed to request keyboard IRQ\n");
return err;
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index dade381c7da8..d6d16fa78281 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -94,11 +94,11 @@ config INPUT_BMA150
module will be called bma150.
config INPUT_E3X0_BUTTON
- tristate "NI Ettus Research USRP E3x0 Button support."
+ tristate "NI Ettus Research USRP E3xx Button support."
default n
help
Say Y here to enable support for the NI Ettus Research
- USRP E3x0 Button.
+ USRP E3xx Button.
To compile this driver as a module, choose M here: the
module will be called e3x0_button.
@@ -167,28 +167,16 @@ config INPUT_M68K_BEEP
depends on M68K
config INPUT_MAX77693_HAPTIC
- tristate "MAXIM MAX77693 haptic controller support"
- depends on MFD_MAX77693 && PWM
+ tristate "MAXIM MAX77693/MAX77843 haptic controller support"
+ depends on (MFD_MAX77693 || MFD_MAX77843) && PWM
select INPUT_FF_MEMLESS
help
This option enables support for the haptic controller on
- MAXIM MAX77693 chip.
+ MAXIM MAX77693 and MAX77843 chips.
To compile this driver as module, choose M here: the
module will be called max77693-haptic.
-config INPUT_MAX77843_HAPTIC
- tristate "MAXIM MAX77843 haptic controller support"
- depends on MFD_MAX77843 && REGULATOR
- select INPUT_FF_MEMLESS
- help
- This option enables support for the haptic controller on
- MAXIM MAX77843 chip. The driver supports ff-memless interface
- from input framework.
-
- To compile this driver as module, choose M here: the
- module will be called max77843-haptic.
-
config INPUT_MAX8925_ONKEY
tristate "MAX8925 ONKEY support"
depends on MFD_MAX8925
@@ -611,11 +599,11 @@ config INPUT_DA9055_ONKEY
will be called da9055_onkey.
config INPUT_DA9063_ONKEY
- tristate "Dialog DA9063 OnKey"
- depends on MFD_DA9063
+ tristate "Dialog DA9062/63 OnKey"
+ depends on MFD_DA9063 || MFD_DA9062
help
- Support the ONKEY of Dialog DA9063 Power Management IC as an
- input device reporting power button statue.
+ Support the ONKEY of Dialog DA9063 and DA9062 Power Management ICs
+ as an input device capable of reporting the power button status.
To compile this driver as a module, choose M here: the module
will be called da9063_onkey.
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 53df07dcc23c..0357a088c6a9 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -41,7 +41,6 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
obj-$(CONFIG_INPUT_MAX77693_HAPTIC) += max77693-haptic.o
-obj-$(CONFIG_INPUT_MAX77843_HAPTIC) += max77843-haptic.o
obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o
obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o
obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o
diff --git a/drivers/input/misc/ad714x-i2c.c b/drivers/input/misc/ad714x-i2c.c
index 189bdc8e91a5..2f047738bc0b 100644
--- a/drivers/input/misc/ad714x-i2c.c
+++ b/drivers/input/misc/ad714x-i2c.c
@@ -85,15 +85,6 @@ static int ad714x_i2c_probe(struct i2c_client *client,
return 0;
}
-static int ad714x_i2c_remove(struct i2c_client *client)
-{
- struct ad714x_chip *chip = i2c_get_clientdata(client);
-
- ad714x_remove(chip);
-
- return 0;
-}
-
static const struct i2c_device_id ad714x_id[] = {
{ "ad7142_captouch", 0 },
{ "ad7143_captouch", 0 },
@@ -110,7 +101,6 @@ static struct i2c_driver ad714x_i2c_driver = {
.pm = &ad714x_i2c_pm,
},
.probe = ad714x_i2c_probe,
- .remove = ad714x_i2c_remove,
.id_table = ad714x_id,
};
diff --git a/drivers/input/misc/ad714x-spi.c b/drivers/input/misc/ad714x-spi.c
index a79e50b58bf5..c8170f082962 100644
--- a/drivers/input/misc/ad714x-spi.c
+++ b/drivers/input/misc/ad714x-spi.c
@@ -101,15 +101,6 @@ static int ad714x_spi_probe(struct spi_device *spi)
return 0;
}
-static int ad714x_spi_remove(struct spi_device *spi)
-{
- struct ad714x_chip *chip = spi_get_drvdata(spi);
-
- ad714x_remove(chip);
-
- return 0;
-}
-
static struct spi_driver ad714x_spi_driver = {
.driver = {
.name = "ad714x_captouch",
@@ -117,7 +108,6 @@ static struct spi_driver ad714x_spi_driver = {
.pm = &ad714x_spi_pm,
},
.probe = ad714x_spi_probe,
- .remove = ad714x_spi_remove,
};
module_spi_driver(ad714x_spi_driver);
diff --git a/drivers/input/misc/ad714x.c b/drivers/input/misc/ad714x.c
index 7a61e9ee682c..84b51dd51f6e 100644
--- a/drivers/input/misc/ad714x.c
+++ b/drivers/input/misc/ad714x.c
@@ -960,13 +960,12 @@ static irqreturn_t ad714x_interrupt_thread(int irq, void *data)
return IRQ_HANDLED;
}
-#define MAX_DEVICE_NUM 8
struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
ad714x_read_t read, ad714x_write_t write)
{
- int i, alloc_idx;
+ int i;
int error;
- struct input_dev *input[MAX_DEVICE_NUM];
+ struct input_dev *input;
struct ad714x_platform_data *plat_data = dev_get_platdata(dev);
struct ad714x_chip *ad714x;
@@ -982,25 +981,25 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
if (irq <= 0) {
dev_err(dev, "IRQ not configured!\n");
error = -EINVAL;
- goto err_out;
+ return ERR_PTR(error);
}
if (dev_get_platdata(dev) == NULL) {
dev_err(dev, "platform data for ad714x doesn't exist\n");
error = -EINVAL;
- goto err_out;
+ return ERR_PTR(error);
}
- ad714x = kzalloc(sizeof(*ad714x) + sizeof(*ad714x->sw) +
- sizeof(*sd_drv) * plat_data->slider_num +
- sizeof(*wl_drv) * plat_data->wheel_num +
- sizeof(*tp_drv) * plat_data->touchpad_num +
- sizeof(*bt_drv) * plat_data->button_num, GFP_KERNEL);
+ ad714x = devm_kzalloc(dev, sizeof(*ad714x) + sizeof(*ad714x->sw) +
+ sizeof(*sd_drv) * plat_data->slider_num +
+ sizeof(*wl_drv) * plat_data->wheel_num +
+ sizeof(*tp_drv) * plat_data->touchpad_num +
+ sizeof(*bt_drv) * plat_data->button_num,
+ GFP_KERNEL);
if (!ad714x) {
error = -ENOMEM;
- goto err_out;
+ return ERR_PTR(error);
}
-
ad714x->hw = plat_data;
drv_mem = ad714x + 1;
@@ -1022,47 +1021,40 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
error = ad714x_hw_detect(ad714x);
if (error)
- goto err_free_mem;
+ return ERR_PTR(error);
/* initialize and request sw/hw resources */
ad714x_hw_init(ad714x);
mutex_init(&ad714x->mutex);
- /*
- * Allocate and register AD714X input device
- */
- alloc_idx = 0;
-
/* a slider uses one input_dev instance */
if (ad714x->hw->slider_num > 0) {
struct ad714x_slider_plat *sd_plat = ad714x->hw->slider;
for (i = 0; i < ad714x->hw->slider_num; i++) {
- sd_drv[i].input = input[alloc_idx] = input_allocate_device();
- if (!input[alloc_idx]) {
- error = -ENOMEM;
- goto err_free_dev;
- }
-
- __set_bit(EV_ABS, input[alloc_idx]->evbit);
- __set_bit(EV_KEY, input[alloc_idx]->evbit);
- __set_bit(ABS_X, input[alloc_idx]->absbit);
- __set_bit(BTN_TOUCH, input[alloc_idx]->keybit);
- input_set_abs_params(input[alloc_idx],
+ input = devm_input_allocate_device(dev);
+ if (!input)
+ return ERR_PTR(-ENOMEM);
+
+ __set_bit(EV_ABS, input->evbit);
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(ABS_X, input->absbit);
+ __set_bit(BTN_TOUCH, input->keybit);
+ input_set_abs_params(input,
ABS_X, 0, sd_plat->max_coord, 0, 0);
- input[alloc_idx]->id.bustype = bus_type;
- input[alloc_idx]->id.product = ad714x->product;
- input[alloc_idx]->id.version = ad714x->version;
- input[alloc_idx]->name = "ad714x_captouch_slider";
- input[alloc_idx]->dev.parent = dev;
+ input->id.bustype = bus_type;
+ input->id.product = ad714x->product;
+ input->id.version = ad714x->version;
+ input->name = "ad714x_captouch_slider";
+ input->dev.parent = dev;
- error = input_register_device(input[alloc_idx]);
+ error = input_register_device(input);
if (error)
- goto err_free_dev;
+ return ERR_PTR(error);
- alloc_idx++;
+ sd_drv[i].input = input;
}
}
@@ -1071,30 +1063,28 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
struct ad714x_wheel_plat *wl_plat = ad714x->hw->wheel;
for (i = 0; i < ad714x->hw->wheel_num; i++) {
- wl_drv[i].input = input[alloc_idx] = input_allocate_device();
- if (!input[alloc_idx]) {
- error = -ENOMEM;
- goto err_free_dev;
- }
-
- __set_bit(EV_KEY, input[alloc_idx]->evbit);
- __set_bit(EV_ABS, input[alloc_idx]->evbit);
- __set_bit(ABS_WHEEL, input[alloc_idx]->absbit);
- __set_bit(BTN_TOUCH, input[alloc_idx]->keybit);
- input_set_abs_params(input[alloc_idx],
+ input = devm_input_allocate_device(dev);
+ if (!input)
+ return ERR_PTR(-ENOMEM);
+
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(EV_ABS, input->evbit);
+ __set_bit(ABS_WHEEL, input->absbit);
+ __set_bit(BTN_TOUCH, input->keybit);
+ input_set_abs_params(input,
ABS_WHEEL, 0, wl_plat->max_coord, 0, 0);
- input[alloc_idx]->id.bustype = bus_type;
- input[alloc_idx]->id.product = ad714x->product;
- input[alloc_idx]->id.version = ad714x->version;
- input[alloc_idx]->name = "ad714x_captouch_wheel";
- input[alloc_idx]->dev.parent = dev;
+ input->id.bustype = bus_type;
+ input->id.product = ad714x->product;
+ input->id.version = ad714x->version;
+ input->name = "ad714x_captouch_wheel";
+ input->dev.parent = dev;
- error = input_register_device(input[alloc_idx]);
+ error = input_register_device(input);
if (error)
- goto err_free_dev;
+ return ERR_PTR(error);
- alloc_idx++;
+ wl_drv[i].input = input;
}
}
@@ -1103,33 +1093,31 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
struct ad714x_touchpad_plat *tp_plat = ad714x->hw->touchpad;
for (i = 0; i < ad714x->hw->touchpad_num; i++) {
- tp_drv[i].input = input[alloc_idx] = input_allocate_device();
- if (!input[alloc_idx]) {
- error = -ENOMEM;
- goto err_free_dev;
- }
-
- __set_bit(EV_ABS, input[alloc_idx]->evbit);
- __set_bit(EV_KEY, input[alloc_idx]->evbit);
- __set_bit(ABS_X, input[alloc_idx]->absbit);
- __set_bit(ABS_Y, input[alloc_idx]->absbit);
- __set_bit(BTN_TOUCH, input[alloc_idx]->keybit);
- input_set_abs_params(input[alloc_idx],
+ input = devm_input_allocate_device(dev);
+ if (!input)
+ return ERR_PTR(-ENOMEM);
+
+ __set_bit(EV_ABS, input->evbit);
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(ABS_X, input->absbit);
+ __set_bit(ABS_Y, input->absbit);
+ __set_bit(BTN_TOUCH, input->keybit);
+ input_set_abs_params(input,
ABS_X, 0, tp_plat->x_max_coord, 0, 0);
- input_set_abs_params(input[alloc_idx],
+ input_set_abs_params(input,
ABS_Y, 0, tp_plat->y_max_coord, 0, 0);
- input[alloc_idx]->id.bustype = bus_type;
- input[alloc_idx]->id.product = ad714x->product;
- input[alloc_idx]->id.version = ad714x->version;
- input[alloc_idx]->name = "ad714x_captouch_pad";
- input[alloc_idx]->dev.parent = dev;
+ input->id.bustype = bus_type;
+ input->id.product = ad714x->product;
+ input->id.version = ad714x->version;
+ input->name = "ad714x_captouch_pad";
+ input->dev.parent = dev;
- error = input_register_device(input[alloc_idx]);
+ error = input_register_device(input);
if (error)
- goto err_free_dev;
+ return ERR_PTR(error);
- alloc_idx++;
+ tp_drv[i].input = input;
}
}
@@ -1137,82 +1125,44 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
if (ad714x->hw->button_num > 0) {
struct ad714x_button_plat *bt_plat = ad714x->hw->button;
- input[alloc_idx] = input_allocate_device();
- if (!input[alloc_idx]) {
+ input = devm_input_allocate_device(dev);
+ if (!input) {
error = -ENOMEM;
- goto err_free_dev;
+ return ERR_PTR(error);
}
- __set_bit(EV_KEY, input[alloc_idx]->evbit);
+ __set_bit(EV_KEY, input->evbit);
for (i = 0; i < ad714x->hw->button_num; i++) {
- bt_drv[i].input = input[alloc_idx];
- __set_bit(bt_plat[i].keycode, input[alloc_idx]->keybit);
+ bt_drv[i].input = input;
+ __set_bit(bt_plat[i].keycode, input->keybit);
}
- input[alloc_idx]->id.bustype = bus_type;
- input[alloc_idx]->id.product = ad714x->product;
- input[alloc_idx]->id.version = ad714x->version;
- input[alloc_idx]->name = "ad714x_captouch_button";
- input[alloc_idx]->dev.parent = dev;
+ input->id.bustype = bus_type;
+ input->id.product = ad714x->product;
+ input->id.version = ad714x->version;
+ input->name = "ad714x_captouch_button";
+ input->dev.parent = dev;
- error = input_register_device(input[alloc_idx]);
+ error = input_register_device(input);
if (error)
- goto err_free_dev;
-
- alloc_idx++;
+ return ERR_PTR(error);
}
irqflags = plat_data->irqflags ?: IRQF_TRIGGER_FALLING;
irqflags |= IRQF_ONESHOT;
- error = request_threaded_irq(ad714x->irq, NULL, ad714x_interrupt_thread,
- irqflags, "ad714x_captouch", ad714x);
+ error = devm_request_threaded_irq(dev, ad714x->irq, NULL,
+ ad714x_interrupt_thread,
+ irqflags, "ad714x_captouch", ad714x);
if (error) {
dev_err(dev, "can't allocate irq %d\n", ad714x->irq);
- goto err_unreg_dev;
+ return ERR_PTR(error);
}
return ad714x;
-
- err_free_dev:
- dev_err(dev, "failed to setup AD714x input device %i\n", alloc_idx);
- input_free_device(input[alloc_idx]);
- err_unreg_dev:
- while (--alloc_idx >= 0)
- input_unregister_device(input[alloc_idx]);
- err_free_mem:
- kfree(ad714x);
- err_out:
- return ERR_PTR(error);
}
EXPORT_SYMBOL(ad714x_probe);
-void ad714x_remove(struct ad714x_chip *ad714x)
-{
- struct ad714x_platform_data *hw = ad714x->hw;
- struct ad714x_driver_data *sw = ad714x->sw;
- int i;
-
- free_irq(ad714x->irq, ad714x);
-
- /* unregister and free all input devices */
-
- for (i = 0; i < hw->slider_num; i++)
- input_unregister_device(sw->slider[i].input);
-
- for (i = 0; i < hw->wheel_num; i++)
- input_unregister_device(sw->wheel[i].input);
-
- for (i = 0; i < hw->touchpad_num; i++)
- input_unregister_device(sw->touchpad[i].input);
-
- if (hw->button_num)
- input_unregister_device(sw->button[0].input);
-
- kfree(ad714x);
-}
-EXPORT_SYMBOL(ad714x_remove);
-
#ifdef CONFIG_PM
int ad714x_disable(struct ad714x_chip *ad714x)
{
diff --git a/drivers/input/misc/ad714x.h b/drivers/input/misc/ad714x.h
index 3c85455aa66d..5d65d303b9bf 100644
--- a/drivers/input/misc/ad714x.h
+++ b/drivers/input/misc/ad714x.h
@@ -50,6 +50,5 @@ int ad714x_disable(struct ad714x_chip *ad714x);
int ad714x_enable(struct ad714x_chip *ad714x);
struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
ad714x_read_t read, ad714x_write_t write);
-void ad714x_remove(struct ad714x_chip *ad714x);
#endif
diff --git a/drivers/input/misc/da9063_onkey.c b/drivers/input/misc/da9063_onkey.c
index f577585ef999..8eb697db82d0 100644
--- a/drivers/input/misc/da9063_onkey.c
+++ b/drivers/input/misc/da9063_onkey.c
@@ -1,5 +1,5 @@
/*
- * OnKey device driver for DA9063
+ * OnKey device driver for DA9063 and DA9062 PMICs
* Copyright (C) 2015 Dialog Semiconductor Ltd.
*
* This program is free software; you can redistribute it and/or
@@ -24,36 +24,96 @@
#include <linux/mfd/da9063/core.h>
#include <linux/mfd/da9063/pdata.h>
#include <linux/mfd/da9063/registers.h>
+#include <linux/mfd/da9062/core.h>
+#include <linux/mfd/da9062/registers.h>
+
+struct da906x_chip_config {
+ /* REGS */
+ int onkey_status;
+ int onkey_pwr_signalling;
+ int onkey_fault_log;
+ int onkey_shutdown;
+ /* MASKS */
+ int onkey_nonkey_mask;
+ int onkey_nonkey_lock_mask;
+ int onkey_key_reset_mask;
+ int onkey_shutdown_mask;
+ /* NAMES */
+ const char *name;
+};
struct da9063_onkey {
- struct da9063 *hw;
struct delayed_work work;
struct input_dev *input;
struct device *dev;
+ struct regmap *regmap;
+ const struct da906x_chip_config *config;
+ char phys[32];
bool key_power;
};
+static const struct da906x_chip_config da9063_regs = {
+ /* REGS */
+ .onkey_status = DA9063_REG_STATUS_A,
+ .onkey_pwr_signalling = DA9063_REG_CONTROL_B,
+ .onkey_fault_log = DA9063_REG_FAULT_LOG,
+ .onkey_shutdown = DA9063_REG_CONTROL_F,
+ /* MASKS */
+ .onkey_nonkey_mask = DA9063_NONKEY,
+ .onkey_nonkey_lock_mask = DA9063_NONKEY_LOCK,
+ .onkey_key_reset_mask = DA9063_KEY_RESET,
+ .onkey_shutdown_mask = DA9063_SHUTDOWN,
+ /* NAMES */
+ .name = DA9063_DRVNAME_ONKEY,
+};
+
+static const struct da906x_chip_config da9062_regs = {
+ /* REGS */
+ .onkey_status = DA9062AA_STATUS_A,
+ .onkey_pwr_signalling = DA9062AA_CONTROL_B,
+ .onkey_fault_log = DA9062AA_FAULT_LOG,
+ .onkey_shutdown = DA9062AA_CONTROL_F,
+ /* MASKS */
+ .onkey_nonkey_mask = DA9062AA_NONKEY_MASK,
+ .onkey_nonkey_lock_mask = DA9062AA_NONKEY_LOCK_MASK,
+ .onkey_key_reset_mask = DA9062AA_KEY_RESET_MASK,
+ .onkey_shutdown_mask = DA9062AA_SHUTDOWN_MASK,
+ /* NAMES */
+ .name = "da9062-onkey",
+};
+
+static const struct of_device_id da9063_compatible_reg_id_table[] = {
+ { .compatible = "dlg,da9063-onkey", .data = &da9063_regs },
+ { .compatible = "dlg,da9062-onkey", .data = &da9062_regs },
+ { },
+};
+
static void da9063_poll_on(struct work_struct *work)
{
- struct da9063_onkey *onkey = container_of(work, struct da9063_onkey,
- work.work);
+ struct da9063_onkey *onkey = container_of(work,
+ struct da9063_onkey,
+ work.work);
+ const struct da906x_chip_config *config = onkey->config;
unsigned int val;
int fault_log = 0;
bool poll = true;
int error;
/* Poll to see when the pin is released */
- error = regmap_read(onkey->hw->regmap, DA9063_REG_STATUS_A, &val);
+ error = regmap_read(onkey->regmap,
+ config->onkey_status,
+ &val);
if (error) {
dev_err(onkey->dev,
"Failed to read ON status: %d\n", error);
goto err_poll;
}
- if (!(val & DA9063_NONKEY)) {
- error = regmap_update_bits(onkey->hw->regmap,
- DA9063_REG_CONTROL_B,
- DA9063_NONKEY_LOCK, 0);
+ if (!(val & config->onkey_nonkey_mask)) {
+ error = regmap_update_bits(onkey->regmap,
+ config->onkey_pwr_signalling,
+ config->onkey_nonkey_lock_mask,
+ 0);
if (error) {
dev_err(onkey->dev,
"Failed to reset the Key Delay %d\n", error);
@@ -70,15 +130,16 @@ static void da9063_poll_on(struct work_struct *work)
* If the fault log KEY_RESET is detected, then clear it
* and shut down the system.
*/
- error = regmap_read(onkey->hw->regmap,
- DA9063_REG_FAULT_LOG, &fault_log);
+ error = regmap_read(onkey->regmap,
+ config->onkey_fault_log,
+ &fault_log);
if (error) {
dev_warn(&onkey->input->dev,
"Cannot read FAULT_LOG: %d\n", error);
- } else if (fault_log & DA9063_KEY_RESET) {
- error = regmap_write(onkey->hw->regmap,
- DA9063_REG_FAULT_LOG,
- DA9063_KEY_RESET);
+ } else if (fault_log & config->onkey_key_reset_mask) {
+ error = regmap_write(onkey->regmap,
+ config->onkey_fault_log,
+ config->onkey_key_reset_mask);
if (error) {
dev_warn(&onkey->input->dev,
"Cannot reset KEY_RESET fault log: %d\n",
@@ -88,10 +149,10 @@ static void da9063_poll_on(struct work_struct *work)
* and then send shutdown command
*/
dev_dbg(&onkey->input->dev,
- "Sending SHUTDOWN to DA9063 ...\n");
- error = regmap_write(onkey->hw->regmap,
- DA9063_REG_CONTROL_F,
- DA9063_SHUTDOWN);
+ "Sending SHUTDOWN to DA9063 ...\n");
+ error = regmap_write(onkey->regmap,
+ config->onkey_shutdown,
+ config->onkey_shutdown_mask);
if (error)
dev_err(&onkey->input->dev,
"Cannot SHUTDOWN DA9063: %d\n",
@@ -107,11 +168,14 @@ err_poll:
static irqreturn_t da9063_onkey_irq_handler(int irq, void *data)
{
struct da9063_onkey *onkey = data;
+ const struct da906x_chip_config *config = onkey->config;
unsigned int val;
int error;
- error = regmap_read(onkey->hw->regmap, DA9063_REG_STATUS_A, &val);
- if (onkey->key_power && !error && (val & DA9063_NONKEY)) {
+ error = regmap_read(onkey->regmap,
+ config->onkey_status,
+ &val);
+ if (onkey->key_power && !error && (val & config->onkey_nonkey_mask)) {
input_report_key(onkey->input, KEY_POWER, 1);
input_sync(onkey->input);
schedule_delayed_work(&onkey->work, 0);
@@ -139,9 +203,15 @@ static int da9063_onkey_probe(struct platform_device *pdev)
struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent);
struct da9063_pdata *pdata = dev_get_platdata(da9063->dev);
struct da9063_onkey *onkey;
+ const struct of_device_id *match;
int irq;
int error;
+ match = of_match_node(da9063_compatible_reg_id_table,
+ pdev->dev.of_node);
+ if (!match)
+ return -ENXIO;
+
onkey = devm_kzalloc(&pdev->dev, sizeof(struct da9063_onkey),
GFP_KERNEL);
if (!onkey) {
@@ -149,8 +219,14 @@ static int da9063_onkey_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ onkey->config = match->data;
onkey->dev = &pdev->dev;
- onkey->hw = da9063;
+
+ onkey->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!onkey->regmap) {
+ dev_err(&pdev->dev, "Parent regmap unavailable.\n");
+ return -ENXIO;
+ }
if (pdata)
onkey->key_power = pdata->key_power;
@@ -165,8 +241,10 @@ static int da9063_onkey_probe(struct platform_device *pdev)
return -ENOMEM;
}
- onkey->input->name = DA9063_DRVNAME_ONKEY;
- onkey->input->phys = DA9063_DRVNAME_ONKEY "/input0";
+ onkey->input->name = onkey->config->name;
+ snprintf(onkey->phys, sizeof(onkey->phys), "%s/input0",
+ onkey->config->name);
+ onkey->input->phys = onkey->phys;
onkey->input->dev.parent = &pdev->dev;
if (onkey->key_power)
@@ -216,11 +294,12 @@ static struct platform_driver da9063_onkey_driver = {
.probe = da9063_onkey_probe,
.driver = {
.name = DA9063_DRVNAME_ONKEY,
+ .of_match_table = da9063_compatible_reg_id_table,
},
};
module_platform_driver(da9063_onkey_driver);
MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>");
-MODULE_DESCRIPTION("Onkey device driver for Dialog DA9063");
+MODULE_DESCRIPTION("Onkey device driver for Dialog DA9063 and DA9062");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DA9063_DRVNAME_ONKEY);
diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
index e65496e6463d..2adfd86c869a 100644
--- a/drivers/input/misc/drv260x.c
+++ b/drivers/input/misc/drv260x.c
@@ -313,14 +313,14 @@ static void drv260x_close(struct input_dev *input)
gpiod_set_value(haptics->enable_gpio, 0);
}
-static const struct reg_default drv260x_lra_cal_regs[] = {
+static const struct reg_sequence drv260x_lra_cal_regs[] = {
{ DRV260X_MODE, DRV260X_AUTO_CAL },
{ DRV260X_CTRL3, DRV260X_NG_THRESH_2 },
{ DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE |
DRV260X_BRAKE_FACTOR_4X | DRV260X_LOOP_GAIN_HIGH },
};
-static const struct reg_default drv260x_lra_init_regs[] = {
+static const struct reg_sequence drv260x_lra_init_regs[] = {
{ DRV260X_MODE, DRV260X_RT_PLAYBACK },
{ DRV260X_A_TO_V_CTRL, DRV260X_AUDIO_HAPTICS_PEAK_20MS |
DRV260X_AUDIO_HAPTICS_FILTER_125HZ },
@@ -337,7 +337,7 @@ static const struct reg_default drv260x_lra_init_regs[] = {
{ DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS },
};
-static const struct reg_default drv260x_erm_cal_regs[] = {
+static const struct reg_sequence drv260x_erm_cal_regs[] = {
{ DRV260X_MODE, DRV260X_AUTO_CAL },
{ DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT },
{ DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT },
diff --git a/drivers/input/misc/drv2665.c b/drivers/input/misc/drv2665.c
index 327a63c2f07d..ef9bc12b3be3 100644
--- a/drivers/input/misc/drv2665.c
+++ b/drivers/input/misc/drv2665.c
@@ -132,7 +132,7 @@ static void drv2665_close(struct input_dev *input)
"Failed to enter standby mode: %d\n", error);
}
-static const struct reg_default drv2665_init_regs[] = {
+static const struct reg_sequence drv2665_init_regs[] = {
{ DRV2665_CTRL_2, 0 | DRV2665_10_MS_IDLE_TOUT },
{ DRV2665_CTRL_1, DRV2665_25_VPP_GAIN },
};
diff --git a/drivers/input/misc/drv2667.c b/drivers/input/misc/drv2667.c
index 46a4be4e9dc3..d5ba7481328c 100644
--- a/drivers/input/misc/drv2667.c
+++ b/drivers/input/misc/drv2667.c
@@ -262,14 +262,14 @@ static void drv2667_close(struct input_dev *input)
"Failed to enter standby mode: %d\n", error);
}
-static const struct reg_default drv2667_init_regs[] = {
+static const struct reg_sequence drv2667_init_regs[] = {
{ DRV2667_CTRL_2, 0 },
{ DRV2667_CTRL_1, DRV2667_25_VPP_GAIN },
{ DRV2667_WV_SEQ_0, 1 },
{ DRV2667_WV_SEQ_1, 0 }
};
-static const struct reg_default drv2667_page1_init[] = {
+static const struct reg_sequence drv2667_page1_init[] = {
{ DRV2667_RAM_HDR_SZ, 0x05 },
{ DRV2667_RAM_START_HI, 0x80 },
{ DRV2667_RAM_START_LO, 0x06 },
diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c
index 45e0e3e55de2..1c8c56efc995 100644
--- a/drivers/input/misc/hp_sdc_rtc.c
+++ b/drivers/input/misc/hp_sdc_rtc.c
@@ -198,7 +198,7 @@ static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg)
/* Read the i8042 real-time clock */
-static inline int hp_sdc_rtc_read_rt(struct timeval *res) {
+static inline int hp_sdc_rtc_read_rt(struct timespec64 *res) {
int64_t raw;
uint32_t tenms;
unsigned int days;
@@ -209,15 +209,15 @@ static inline int hp_sdc_rtc_read_rt(struct timeval *res) {
tenms = (uint32_t)raw & 0xffffff;
days = (unsigned int)(raw >> 24) & 0xffff;
- res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
- res->tv_sec = (time_t)(tenms / 100) + days * 86400;
+ res->tv_nsec = (long)(tenms % 100) * 10000 * 1000;
+ res->tv_sec = (tenms / 100) + (time64_t)days * 86400;
return 0;
}
/* Read the i8042 fast handshake timer */
-static inline int hp_sdc_rtc_read_fhs(struct timeval *res) {
+static inline int hp_sdc_rtc_read_fhs(struct timespec64 *res) {
int64_t raw;
unsigned int tenms;
@@ -226,15 +226,15 @@ static inline int hp_sdc_rtc_read_fhs(struct timeval *res) {
tenms = (unsigned int)raw & 0xffff;
- res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
- res->tv_sec = (time_t)(tenms / 100);
+ res->tv_nsec = (long)(tenms % 100) * 10000 * 1000;
+ res->tv_sec = (time64_t)(tenms / 100);
return 0;
}
/* Read the i8042 match timer (a.k.a. alarm) */
-static inline int hp_sdc_rtc_read_mt(struct timeval *res) {
+static inline int hp_sdc_rtc_read_mt(struct timespec64 *res) {
int64_t raw;
uint32_t tenms;
@@ -243,15 +243,15 @@ static inline int hp_sdc_rtc_read_mt(struct timeval *res) {
tenms = (uint32_t)raw & 0xffffff;
- res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
- res->tv_sec = (time_t)(tenms / 100);
+ res->tv_nsec = (long)(tenms % 100) * 10000 * 1000;
+ res->tv_sec = (time64_t)(tenms / 100);
return 0;
}
/* Read the i8042 delay timer */
-static inline int hp_sdc_rtc_read_dt(struct timeval *res) {
+static inline int hp_sdc_rtc_read_dt(struct timespec64 *res) {
int64_t raw;
uint32_t tenms;
@@ -260,15 +260,15 @@ static inline int hp_sdc_rtc_read_dt(struct timeval *res) {
tenms = (uint32_t)raw & 0xffffff;
- res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
- res->tv_sec = (time_t)(tenms / 100);
+ res->tv_nsec = (long)(tenms % 100) * 10000 * 1000;
+ res->tv_sec = (time64_t)(tenms / 100);
return 0;
}
/* Read the i8042 cycle timer (a.k.a. periodic) */
-static inline int hp_sdc_rtc_read_ct(struct timeval *res) {
+static inline int hp_sdc_rtc_read_ct(struct timespec64 *res) {
int64_t raw;
uint32_t tenms;
@@ -277,8 +277,8 @@ static inline int hp_sdc_rtc_read_ct(struct timeval *res) {
tenms = (uint32_t)raw & 0xffffff;
- res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
- res->tv_sec = (time_t)(tenms / 100);
+ res->tv_nsec = (long)(tenms % 100) * 10000 * 1000;
+ res->tv_sec = (time64_t)(tenms / 100);
return 0;
}
@@ -433,7 +433,7 @@ static int hp_sdc_rtc_proc_show(struct seq_file *m, void *v)
#define YN(bit) ("no")
#define NY(bit) ("yes")
struct rtc_time tm;
- struct timeval tv;
+ struct timespec64 tv;
memset(&tm, 0, sizeof(struct rtc_time));
@@ -452,36 +452,36 @@ static int hp_sdc_rtc_proc_show(struct seq_file *m, void *v)
if (hp_sdc_rtc_read_rt(&tv)) {
seq_puts(m, "i8042 rtc\t: READ FAILED!\n");
} else {
- seq_printf(m, "i8042 rtc\t: %ld.%02d seconds\n",
- tv.tv_sec, (int)tv.tv_usec/1000);
+ seq_printf(m, "i8042 rtc\t: %lld.%02ld seconds\n",
+ (s64)tv.tv_sec, (long)tv.tv_nsec/1000000L);
}
if (hp_sdc_rtc_read_fhs(&tv)) {
seq_puts(m, "handshake\t: READ FAILED!\n");
} else {
- seq_printf(m, "handshake\t: %ld.%02d seconds\n",
- tv.tv_sec, (int)tv.tv_usec/1000);
+ seq_printf(m, "handshake\t: %lld.%02ld seconds\n",
+ (s64)tv.tv_sec, (long)tv.tv_nsec/1000000L);
}
if (hp_sdc_rtc_read_mt(&tv)) {
seq_puts(m, "alarm\t\t: READ FAILED!\n");
} else {
- seq_printf(m, "alarm\t\t: %ld.%02d seconds\n",
- tv.tv_sec, (int)tv.tv_usec/1000);
+ seq_printf(m, "alarm\t\t: %lld.%02ld seconds\n",
+ (s64)tv.tv_sec, (long)tv.tv_nsec/1000000L);
}
if (hp_sdc_rtc_read_dt(&tv)) {
seq_puts(m, "delay\t\t: READ FAILED!\n");
} else {
- seq_printf(m, "delay\t\t: %ld.%02d seconds\n",
- tv.tv_sec, (int)tv.tv_usec/1000);
+ seq_printf(m, "delay\t\t: %lld.%02ld seconds\n",
+ (s64)tv.tv_sec, (long)tv.tv_nsec/1000000L);
}
if (hp_sdc_rtc_read_ct(&tv)) {
seq_puts(m, "periodic\t: READ FAILED!\n");
} else {
- seq_printf(m, "periodic\t: %ld.%02d seconds\n",
- tv.tv_sec, (int)tv.tv_usec/1000);
+ seq_printf(m, "periodic\t: %lld.%02ld seconds\n",
+ (s64)tv.tv_sec, (long)tv.tv_nsec/1000000L);
}
seq_printf(m,
diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c
index e058d711256a..efaffcc57e36 100644
--- a/drivers/input/misc/kxtj9.c
+++ b/drivers/input/misc/kxtj9.c
@@ -635,7 +635,6 @@ static int __maybe_unused kxtj9_resume(struct device *dev)
struct i2c_client *client = to_i2c_client(dev);
struct kxtj9_data *tj9 = i2c_get_clientdata(client);
struct input_dev *input_dev = tj9->input_dev;
- int retval = 0;
mutex_lock(&input_dev->mutex);
@@ -643,7 +642,7 @@ static int __maybe_unused kxtj9_resume(struct device *dev)
kxtj9_enable(tj9);
mutex_unlock(&input_dev->mutex);
- return retval;
+ return 0;
}
static SIMPLE_DEV_PM_OPS(kxtj9_pm_ops, kxtj9_suspend, kxtj9_resume);
diff --git a/drivers/input/misc/max77693-haptic.c b/drivers/input/misc/max77693-haptic.c
index 39e930c10ebb..6d96bff32a0e 100644
--- a/drivers/input/misc/max77693-haptic.c
+++ b/drivers/input/misc/max77693-haptic.c
@@ -1,8 +1,9 @@
/*
- * MAXIM MAX77693 Haptic device driver
+ * MAXIM MAX77693/MAX77843 Haptic device driver
*
- * Copyright (C) 2014 Samsung Electronics
+ * Copyright (C) 2014,2015 Samsung Electronics
* Jaewon Kim <jaewon02.kim@samsung.com>
+ * Krzysztof Kozlowski <k.kozlowski@samsung.com>
*
* This program is not provided / owned by Maxim Integrated Products.
*
@@ -24,7 +25,9 @@
#include <linux/workqueue.h>
#include <linux/regulator/consumer.h>
#include <linux/mfd/max77693.h>
+#include <linux/mfd/max77693-common.h>
#include <linux/mfd/max77693-private.h>
+#include <linux/mfd/max77843-private.h>
#define MAX_MAGNITUDE_SHIFT 16
@@ -46,6 +49,8 @@ enum max77693_haptic_pwm_divisor {
};
struct max77693_haptic {
+ enum max77693_types dev_type;
+
struct regmap *regmap_pmic;
struct regmap *regmap_haptic;
struct device *dev;
@@ -59,7 +64,6 @@ struct max77693_haptic {
unsigned int pwm_duty;
enum max77693_haptic_motor_type type;
enum max77693_haptic_pulse_mode mode;
- enum max77693_haptic_pwm_divisor pwm_divisor;
struct work_struct work;
};
@@ -78,19 +82,52 @@ static int max77693_haptic_set_duty_cycle(struct max77693_haptic *haptic)
return 0;
}
+static int max77843_haptic_bias(struct max77693_haptic *haptic, bool on)
+{
+ int error;
+
+ if (haptic->dev_type != TYPE_MAX77843)
+ return 0;
+
+ error = regmap_update_bits(haptic->regmap_haptic,
+ MAX77843_SYS_REG_MAINCTRL1,
+ MAX77843_MAINCTRL1_BIASEN_MASK,
+ on << MAINCTRL1_BIASEN_SHIFT);
+ if (error) {
+ dev_err(haptic->dev, "failed to %s bias: %d\n",
+ on ? "enable" : "disable", error);
+ return error;
+ }
+
+ return 0;
+}
+
static int max77693_haptic_configure(struct max77693_haptic *haptic,
bool enable)
{
- unsigned int value;
+ unsigned int value, config_reg;
int error;
- value = ((haptic->type << MAX77693_CONFIG2_MODE) |
- (enable << MAX77693_CONFIG2_MEN) |
- (haptic->mode << MAX77693_CONFIG2_HTYP) |
- (haptic->pwm_divisor));
+ switch (haptic->dev_type) {
+ case TYPE_MAX77693:
+ value = ((haptic->type << MAX77693_CONFIG2_MODE) |
+ (enable << MAX77693_CONFIG2_MEN) |
+ (haptic->mode << MAX77693_CONFIG2_HTYP) |
+ MAX77693_HAPTIC_PWM_DIVISOR_128);
+ config_reg = MAX77693_HAPTIC_REG_CONFIG2;
+ break;
+ case TYPE_MAX77843:
+ value = (haptic->type << MCONFIG_MODE_SHIFT) |
+ (enable << MCONFIG_MEN_SHIFT) |
+ MAX77693_HAPTIC_PWM_DIVISOR_128;
+ config_reg = MAX77843_HAP_REG_MCONFIG;
+ break;
+ default:
+ return -EINVAL;
+ }
error = regmap_write(haptic->regmap_haptic,
- MAX77693_HAPTIC_REG_CONFIG2, value);
+ config_reg, value);
if (error) {
dev_err(haptic->dev,
"failed to update haptic config: %d\n", error);
@@ -104,6 +141,9 @@ static int max77693_haptic_lowsys(struct max77693_haptic *haptic, bool enable)
{
int error;
+ if (haptic->dev_type != TYPE_MAX77693)
+ return 0;
+
error = regmap_update_bits(haptic->regmap_pmic,
MAX77693_PMIC_REG_LSCNFG,
MAX77693_PMIC_LOW_SYS_MASK,
@@ -219,6 +259,10 @@ static int max77693_haptic_open(struct input_dev *dev)
struct max77693_haptic *haptic = input_get_drvdata(dev);
int error;
+ error = max77843_haptic_bias(haptic, true);
+ if (error)
+ return error;
+
error = regulator_enable(haptic->motor_reg);
if (error) {
dev_err(haptic->dev,
@@ -241,6 +285,8 @@ static void max77693_haptic_close(struct input_dev *dev)
if (error)
dev_err(haptic->dev,
"failed to disable regulator: %d\n", error);
+
+ max77843_haptic_bias(haptic, false);
}
static int max77693_haptic_probe(struct platform_device *pdev)
@@ -254,13 +300,26 @@ static int max77693_haptic_probe(struct platform_device *pdev)
return -ENOMEM;
haptic->regmap_pmic = max77693->regmap;
- haptic->regmap_haptic = max77693->regmap_haptic;
haptic->dev = &pdev->dev;
haptic->type = MAX77693_HAPTIC_LRA;
haptic->mode = MAX77693_HAPTIC_EXTERNAL_MODE;
- haptic->pwm_divisor = MAX77693_HAPTIC_PWM_DIVISOR_128;
haptic->suspend_state = false;
+ /* Variant-specific init */
+ haptic->dev_type = platform_get_device_id(pdev)->driver_data;
+ switch (haptic->dev_type) {
+ case TYPE_MAX77693:
+ haptic->regmap_haptic = max77693->regmap_haptic;
+ break;
+ case TYPE_MAX77843:
+ haptic->regmap_haptic = max77693->regmap;
+ break;
+ default:
+ dev_err(&pdev->dev, "unsupported device type: %u\n",
+ haptic->dev_type);
+ return -EINVAL;
+ }
+
INIT_WORK(&haptic->work, max77693_haptic_play_work);
/* Get pwm and regulatot for haptic device */
@@ -338,16 +397,25 @@ static int __maybe_unused max77693_haptic_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(max77693_haptic_pm_ops,
max77693_haptic_suspend, max77693_haptic_resume);
+static const struct platform_device_id max77693_haptic_id[] = {
+ { "max77693-haptic", TYPE_MAX77693 },
+ { "max77843-haptic", TYPE_MAX77843 },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, max77693_haptic_id);
+
static struct platform_driver max77693_haptic_driver = {
.driver = {
.name = "max77693-haptic",
.pm = &max77693_haptic_pm_ops,
},
.probe = max77693_haptic_probe,
+ .id_table = max77693_haptic_id,
};
module_platform_driver(max77693_haptic_driver);
MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>");
-MODULE_DESCRIPTION("MAXIM MAX77693 Haptic driver");
+MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>");
+MODULE_DESCRIPTION("MAXIM 77693/77843 Haptic driver");
MODULE_ALIAS("platform:max77693-haptic");
MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/max77843-haptic.c b/drivers/input/misc/max77843-haptic.c
deleted file mode 100644
index dccbb465a055..000000000000
--- a/drivers/input/misc/max77843-haptic.c
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * MAXIM MAX77693 Haptic device driver
- *
- * Copyright (C) 2015 Samsung Electronics
- * Author: Jaewon Kim <jaewon02.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 as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <linux/err.h>
-#include <linux/i2c.h>
-#include <linux/init.h>
-#include <linux/input.h>
-#include <linux/mfd/max77843-private.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/pwm.h>
-#include <linux/regmap.h>
-#include <linux/regulator/consumer.h>
-#include <linux/slab.h>
-#include <linux/workqueue.h>
-
-#define MAX_MAGNITUDE_SHIFT 16
-
-enum max77843_haptic_motor_type {
- MAX77843_HAPTIC_ERM = 0,
- MAX77843_HAPTIC_LRA,
-};
-
-enum max77843_haptic_pwm_divisor {
- MAX77843_HAPTIC_PWM_DIVISOR_32 = 0,
- MAX77843_HAPTIC_PWM_DIVISOR_64,
- MAX77843_HAPTIC_PWM_DIVISOR_128,
- MAX77843_HAPTIC_PWM_DIVISOR_256,
-};
-
-struct max77843_haptic {
- struct regmap *regmap_haptic;
- struct device *dev;
- struct input_dev *input_dev;
- struct pwm_device *pwm_dev;
- struct regulator *motor_reg;
- struct work_struct work;
- struct mutex mutex;
-
- unsigned int magnitude;
- unsigned int pwm_duty;
-
- bool active;
- bool suspended;
-
- enum max77843_haptic_motor_type type;
- enum max77843_haptic_pwm_divisor pwm_divisor;
-};
-
-static int max77843_haptic_set_duty_cycle(struct max77843_haptic *haptic)
-{
- int delta = (haptic->pwm_dev->period + haptic->pwm_duty) / 2;
- int error;
-
- error = pwm_config(haptic->pwm_dev, delta, haptic->pwm_dev->period);
- if (error) {
- dev_err(haptic->dev, "failed to configure pwm: %d\n", error);
- return error;
- }
-
- return 0;
-}
-
-static int max77843_haptic_bias(struct max77843_haptic *haptic, bool on)
-{
- int error;
-
- error = regmap_update_bits(haptic->regmap_haptic,
- MAX77843_SYS_REG_MAINCTRL1,
- MAX77843_MAINCTRL1_BIASEN_MASK,
- on << MAINCTRL1_BIASEN_SHIFT);
- if (error) {
- dev_err(haptic->dev, "failed to %s bias: %d\n",
- on ? "enable" : "disable", error);
- return error;
- }
-
- return 0;
-}
-
-static int max77843_haptic_config(struct max77843_haptic *haptic, bool enable)
-{
- unsigned int value;
- int error;
-
- value = (haptic->type << MCONFIG_MODE_SHIFT) |
- (enable << MCONFIG_MEN_SHIFT) |
- (haptic->pwm_divisor << MCONFIG_PDIV_SHIFT);
-
- error = regmap_write(haptic->regmap_haptic,
- MAX77843_HAP_REG_MCONFIG, value);
- if (error) {
- dev_err(haptic->dev,
- "failed to update haptic config: %d\n", error);
- return error;
- }
-
- return 0;
-}
-
-static int max77843_haptic_enable(struct max77843_haptic *haptic)
-{
- int error;
-
- if (haptic->active)
- return 0;
-
- error = pwm_enable(haptic->pwm_dev);
- if (error) {
- dev_err(haptic->dev,
- "failed to enable pwm device: %d\n", error);
- return error;
- }
-
- error = max77843_haptic_config(haptic, true);
- if (error)
- goto err_config;
-
- haptic->active = true;
-
- return 0;
-
-err_config:
- pwm_disable(haptic->pwm_dev);
-
- return error;
-}
-
-static int max77843_haptic_disable(struct max77843_haptic *haptic)
-{
- int error;
-
- if (!haptic->active)
- return 0;
-
- error = max77843_haptic_config(haptic, false);
- if (error)
- return error;
-
- pwm_disable(haptic->pwm_dev);
-
- haptic->active = false;
-
- return 0;
-}
-
-static void max77843_haptic_play_work(struct work_struct *work)
-{
- struct max77843_haptic *haptic =
- container_of(work, struct max77843_haptic, work);
- int error;
-
- mutex_lock(&haptic->mutex);
-
- if (haptic->suspended)
- goto out_unlock;
-
- if (haptic->magnitude) {
- error = max77843_haptic_set_duty_cycle(haptic);
- if (error) {
- dev_err(haptic->dev,
- "failed to set duty cycle: %d\n", error);
- goto out_unlock;
- }
-
- error = max77843_haptic_enable(haptic);
- if (error)
- dev_err(haptic->dev,
- "cannot enable haptic: %d\n", error);
- } else {
- error = max77843_haptic_disable(haptic);
- if (error)
- dev_err(haptic->dev,
- "cannot disable haptic: %d\n", error);
- }
-
-out_unlock:
- mutex_unlock(&haptic->mutex);
-}
-
-static int max77843_haptic_play_effect(struct input_dev *dev, void *data,
- struct ff_effect *effect)
-{
- struct max77843_haptic *haptic = input_get_drvdata(dev);
- u64 period_mag_multi;
-
- haptic->magnitude = effect->u.rumble.strong_magnitude;
- if (!haptic->magnitude)
- haptic->magnitude = effect->u.rumble.weak_magnitude;
-
- period_mag_multi = (u64)haptic->pwm_dev->period * haptic->magnitude;
- haptic->pwm_duty = (unsigned int)(period_mag_multi >>
- MAX_MAGNITUDE_SHIFT);
-
- schedule_work(&haptic->work);
-
- return 0;
-}
-
-static int max77843_haptic_open(struct input_dev *dev)
-{
- struct max77843_haptic *haptic = input_get_drvdata(dev);
- int error;
-
- error = max77843_haptic_bias(haptic, true);
- if (error)
- return error;
-
- error = regulator_enable(haptic->motor_reg);
- if (error) {
- dev_err(haptic->dev,
- "failed to enable regulator: %d\n", error);
- return error;
- }
-
- return 0;
-}
-
-static void max77843_haptic_close(struct input_dev *dev)
-{
- struct max77843_haptic *haptic = input_get_drvdata(dev);
- int error;
-
- cancel_work_sync(&haptic->work);
- max77843_haptic_disable(haptic);
-
- error = regulator_disable(haptic->motor_reg);
- if (error)
- dev_err(haptic->dev,
- "failed to disable regulator: %d\n", error);
-
- max77843_haptic_bias(haptic, false);
-}
-
-static int max77843_haptic_probe(struct platform_device *pdev)
-{
- struct max77843 *max77843 = dev_get_drvdata(pdev->dev.parent);
- struct max77843_haptic *haptic;
- int error;
-
- haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL);
- if (!haptic)
- return -ENOMEM;
-
- haptic->regmap_haptic = max77843->regmap;
- haptic->dev = &pdev->dev;
- haptic->type = MAX77843_HAPTIC_LRA;
- haptic->pwm_divisor = MAX77843_HAPTIC_PWM_DIVISOR_128;
-
- INIT_WORK(&haptic->work, max77843_haptic_play_work);
- mutex_init(&haptic->mutex);
-
- haptic->pwm_dev = devm_pwm_get(&pdev->dev, NULL);
- if (IS_ERR(haptic->pwm_dev)) {
- dev_err(&pdev->dev, "failed to get pwm device\n");
- return PTR_ERR(haptic->pwm_dev);
- }
-
- haptic->motor_reg = devm_regulator_get_exclusive(&pdev->dev, "haptic");
- if (IS_ERR(haptic->motor_reg)) {
- dev_err(&pdev->dev, "failed to get regulator\n");
- return PTR_ERR(haptic->motor_reg);
- }
-
- haptic->input_dev = devm_input_allocate_device(&pdev->dev);
- if (!haptic->input_dev) {
- dev_err(&pdev->dev, "failed to allocate input device\n");
- return -ENOMEM;
- }
-
- haptic->input_dev->name = "max77843-haptic";
- haptic->input_dev->id.version = 1;
- haptic->input_dev->dev.parent = &pdev->dev;
- haptic->input_dev->open = max77843_haptic_open;
- haptic->input_dev->close = max77843_haptic_close;
- input_set_drvdata(haptic->input_dev, haptic);
- input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE);
-
- error = input_ff_create_memless(haptic->input_dev, NULL,
- max77843_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;
- }
-
- platform_set_drvdata(pdev, haptic);
-
- return 0;
-}
-
-static int __maybe_unused max77843_haptic_suspend(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct max77843_haptic *haptic = platform_get_drvdata(pdev);
- int error;
-
- error = mutex_lock_interruptible(&haptic->mutex);
- if (error)
- return error;
-
- max77843_haptic_disable(haptic);
-
- haptic->suspended = true;
-
- mutex_unlock(&haptic->mutex);
-
- return 0;
-}
-
-static int __maybe_unused max77843_haptic_resume(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct max77843_haptic *haptic = platform_get_drvdata(pdev);
- unsigned int magnitude;
-
- mutex_lock(&haptic->mutex);
-
- haptic->suspended = false;
-
- magnitude = ACCESS_ONCE(haptic->magnitude);
- if (magnitude)
- max77843_haptic_enable(haptic);
-
- mutex_unlock(&haptic->mutex);
-
- return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(max77843_haptic_pm_ops,
- max77843_haptic_suspend, max77843_haptic_resume);
-
-static struct platform_driver max77843_haptic_driver = {
- .driver = {
- .name = "max77843-haptic",
- .pm = &max77843_haptic_pm_ops,
- },
- .probe = max77843_haptic_probe,
-};
-module_platform_driver(max77843_haptic_driver);
-
-MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>");
-MODULE_DESCRIPTION("MAXIM MAX77843 Haptic driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/rb532_button.c b/drivers/input/misc/rb532_button.c
index e956e81cd4e6..62c5814c796b 100644
--- a/drivers/input/misc/rb532_button.c
+++ b/drivers/input/misc/rb532_button.c
@@ -7,6 +7,7 @@
#include <linux/input-polldev.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/gpio.h>
#include <asm/mach-rc32434/gpio.h>
#include <asm/mach-rc32434/rb.h>
diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c
index f27f81ee84ed..8aee71986430 100644
--- a/drivers/input/misc/rotary_encoder.c
+++ b/drivers/input/misc/rotary_encoder.c
@@ -26,6 +26,7 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
+#include <linux/pm.h>
#define DRV_NAME "rotary-encoder"
@@ -142,6 +143,55 @@ static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static irqreturn_t rotary_encoder_quarter_period_irq(int irq, void *dev_id)
+{
+ struct rotary_encoder *encoder = dev_id;
+ unsigned char sum;
+ int state;
+
+ state = rotary_encoder_get_state(encoder->pdata);
+
+ /*
+ * We encode the previous and the current state using a byte.
+ * The previous state in the MSB nibble, the current state in the LSB
+ * nibble. Then use a table to decide the direction of the turn.
+ */
+ sum = (encoder->last_stable << 4) + state;
+ switch (sum) {
+ case 0x31:
+ case 0x10:
+ case 0x02:
+ case 0x23:
+ encoder->dir = 0; /* clockwise */
+ break;
+
+ case 0x13:
+ case 0x01:
+ case 0x20:
+ case 0x32:
+ encoder->dir = 1; /* counter-clockwise */
+ break;
+
+ default:
+ /*
+ * Ignore all other values. This covers the case when the
+ * state didn't change (a spurious interrupt) and the
+ * cases where the state changed by two steps, making it
+ * impossible to tell the direction.
+ *
+ * In either case, don't report any event and save the
+ * state for later.
+ */
+ goto out;
+ }
+
+ rotary_encoder_report_event(encoder);
+
+out:
+ encoder->last_stable = state;
+ return IRQ_HANDLED;
+}
+
#ifdef CONFIG_OF
static const struct of_device_id rotary_encoder_of_match[] = {
{ .compatible = "rotary-encoder", },
@@ -156,6 +206,7 @@ static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct devic
struct device_node *np = dev->of_node;
struct rotary_encoder_platform_data *pdata;
enum of_gpio_flags flags;
+ int error;
if (!of_id || !np)
return NULL;
@@ -174,12 +225,27 @@ static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct devic
pdata->gpio_b = of_get_gpio_flags(np, 1, &flags);
pdata->inverted_b = flags & OF_GPIO_ACTIVE_LOW;
- pdata->relative_axis = !!of_get_property(np,
- "rotary-encoder,relative-axis", NULL);
- pdata->rollover = !!of_get_property(np,
- "rotary-encoder,rollover", NULL);
- pdata->half_period = !!of_get_property(np,
- "rotary-encoder,half-period", NULL);
+ pdata->relative_axis =
+ of_property_read_bool(np, "rotary-encoder,relative-axis");
+ pdata->rollover = of_property_read_bool(np, "rotary-encoder,rollover");
+
+ error = of_property_read_u32(np, "rotary-encoder,steps-per-period",
+ &pdata->steps_per_period);
+ if (error) {
+ /*
+ * The 'half-period' property has been deprecated, you must use
+ * 'steps-per-period' and set an appropriate value, but we still
+ * need to parse it to maintain compatibility.
+ */
+ if (of_property_read_bool(np, "rotary-encoder,half-period")) {
+ pdata->steps_per_period = 2;
+ } else {
+ /* Fallback to one step per period behavior */
+ pdata->steps_per_period = 1;
+ }
+ }
+
+ pdata->wakeup_source = of_property_read_bool(np, "wakeup-source");
return pdata;
}
@@ -250,12 +316,23 @@ static int rotary_encoder_probe(struct platform_device *pdev)
encoder->irq_a = gpio_to_irq(pdata->gpio_a);
encoder->irq_b = gpio_to_irq(pdata->gpio_b);
- /* request the IRQs */
- if (pdata->half_period) {
+ switch (pdata->steps_per_period) {
+ case 4:
+ handler = &rotary_encoder_quarter_period_irq;
+ encoder->last_stable = rotary_encoder_get_state(pdata);
+ break;
+ case 2:
handler = &rotary_encoder_half_period_irq;
encoder->last_stable = rotary_encoder_get_state(pdata);
- } else {
+ break;
+ case 1:
handler = &rotary_encoder_irq;
+ break;
+ default:
+ dev_err(dev, "'%d' is not a valid steps-per-period value\n",
+ pdata->steps_per_period);
+ err = -EINVAL;
+ goto exit_free_gpio_b;
}
err = request_irq(encoder->irq_a, handler,
@@ -280,6 +357,8 @@ static int rotary_encoder_probe(struct platform_device *pdev)
goto exit_free_irq_b;
}
+ device_init_wakeup(&pdev->dev, pdata->wakeup_source);
+
platform_set_drvdata(pdev, encoder);
return 0;
@@ -306,6 +385,8 @@ static int rotary_encoder_remove(struct platform_device *pdev)
struct rotary_encoder *encoder = platform_get_drvdata(pdev);
const struct rotary_encoder_platform_data *pdata = encoder->pdata;
+ device_init_wakeup(&pdev->dev, false);
+
free_irq(encoder->irq_a, encoder);
free_irq(encoder->irq_b, encoder);
gpio_free(pdata->gpio_a);
@@ -320,11 +401,41 @@ static int rotary_encoder_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int rotary_encoder_suspend(struct device *dev)
+{
+ struct rotary_encoder *encoder = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev)) {
+ enable_irq_wake(encoder->irq_a);
+ enable_irq_wake(encoder->irq_b);
+ }
+
+ return 0;
+}
+
+static int rotary_encoder_resume(struct device *dev)
+{
+ struct rotary_encoder *encoder = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev)) {
+ disable_irq_wake(encoder->irq_a);
+ disable_irq_wake(encoder->irq_b);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(rotary_encoder_pm_ops,
+ rotary_encoder_suspend, rotary_encoder_resume);
+
static struct platform_driver rotary_encoder_driver = {
.probe = rotary_encoder_probe,
.remove = rotary_encoder_remove,
.driver = {
.name = DRV_NAME,
+ .pm = &rotary_encoder_pm_ops,
.of_match_table = of_match_ptr(rotary_encoder_of_match),
}
};
diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c
index 95599e478e19..0a9ad2cfb55c 100644
--- a/drivers/input/misc/xen-kbdfront.c
+++ b/drivers/input/misc/xen-kbdfront.c
@@ -129,8 +129,14 @@ static int xenkbd_probe(struct xenbus_device *dev,
if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-abs-pointer", "%d", &abs) < 0)
abs = 0;
- if (abs)
- xenbus_printf(XBT_NIL, dev->nodename, "request-abs-pointer", "1");
+ if (abs) {
+ ret = xenbus_printf(XBT_NIL, dev->nodename,
+ "request-abs-pointer", "1");
+ if (ret) {
+ pr_warning("xenkbd: can't request abs-pointer");
+ abs = 0;
+ }
+ }
/* keyboard */
kbd = input_allocate_device();
@@ -232,7 +238,7 @@ static int xenkbd_connect_backend(struct xenbus_device *dev,
struct xenbus_transaction xbt;
ret = gnttab_grant_foreign_access(dev->otherend_id,
- virt_to_mfn(info->page), 0);
+ virt_to_gfn(info->page), 0);
if (ret < 0)
return ret;
info->gref = ret;
@@ -255,7 +261,7 @@ static int xenkbd_connect_backend(struct xenbus_device *dev,
goto error_irqh;
}
ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu",
- virt_to_mfn(info->page));
+ virt_to_gfn(info->page));
if (ret)
goto error_xenbus;
ret = xenbus_printf(xbt, dev->nodename, "page-gref", "%u", info->gref);
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 200841b77edb..c3d05b4d3118 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -292,4 +292,18 @@ config SERIO_SUN4I_PS2
To compile this driver as a module, choose M here: the
module will be called sun4i-ps2.
+config USERIO
+ tristate "User space serio port driver support"
+ help
+ Say Y here if you want to support user level drivers for serio
+ subsystem accessible under char device 10:240 - /dev/userio. Using
+ this facility userspace programs can implement serio ports that
+ will be used by the standard in-kernel serio consumer drivers,
+ such as psmouse and atkbd.
+
+ To compile this driver as a module, choose M here: the module will be
+ called userio.
+
+ If you are unsure, say N.
+
endif
diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
index c600089b7a34..2374ef9b33d7 100644
--- a/drivers/input/serio/Makefile
+++ b/drivers/input/serio/Makefile
@@ -30,3 +30,4 @@ 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
+obj-$(CONFIG_USERIO) += userio.o
diff --git a/drivers/input/serio/parkbd.c b/drivers/input/serio/parkbd.c
index 1e8cd6f1fe9e..74bb17270255 100644
--- a/drivers/input/serio/parkbd.c
+++ b/drivers/input/serio/parkbd.c
@@ -141,19 +141,15 @@ static void parkbd_interrupt(void *dev_id)
parkbd_last = jiffies;
}
-static int parkbd_getport(void)
+static int parkbd_getport(struct parport *pp)
{
- struct parport *pp;
+ struct pardev_cb parkbd_parport_cb;
- pp = parport_find_number(parkbd_pp_no);
+ parkbd_parport_cb.irq_func = parkbd_interrupt;
+ parkbd_parport_cb.flags = PARPORT_FLAG_EXCL;
- if (pp == NULL) {
- printk(KERN_ERR "parkbd: no such parport\n");
- return -ENODEV;
- }
-
- parkbd_dev = parport_register_device(pp, "parkbd", NULL, NULL, parkbd_interrupt, PARPORT_DEV_EXCL, NULL);
- parport_put_port(pp);
+ parkbd_dev = parport_register_dev_model(pp, "parkbd",
+ &parkbd_parport_cb, 0);
if (!parkbd_dev)
return -ENODEV;
@@ -183,19 +179,21 @@ static struct serio * __init parkbd_allocate_serio(void)
return serio;
}
-static int __init parkbd_init(void)
+static void parkbd_attach(struct parport *pp)
{
- int err;
+ if (pp->number != parkbd_pp_no) {
+ pr_debug("Not using parport%d.\n", pp->number);
+ return;
+ }
- err = parkbd_getport();
- if (err)
- return err;
+ if (parkbd_getport(pp))
+ return;
parkbd_port = parkbd_allocate_serio();
if (!parkbd_port) {
parport_release(parkbd_dev);
parport_unregister_device(parkbd_dev);
- return -ENOMEM;
+ return;
}
parkbd_writelines(3);
@@ -205,14 +203,35 @@ static int __init parkbd_init(void)
printk(KERN_INFO "serio: PARKBD %s adapter on %s\n",
parkbd_mode ? "AT" : "XT", parkbd_dev->port->name);
- return 0;
+ return;
}
-static void __exit parkbd_exit(void)
+static void parkbd_detach(struct parport *port)
{
+ if (!parkbd_port || port->number != parkbd_pp_no)
+ return;
+
parport_release(parkbd_dev);
serio_unregister_port(parkbd_port);
parport_unregister_device(parkbd_dev);
+ parkbd_port = NULL;
+}
+
+static struct parport_driver parkbd_parport_driver = {
+ .name = "parkbd",
+ .match_port = parkbd_attach,
+ .detach = parkbd_detach,
+ .devmodel = true,
+};
+
+static int __init parkbd_init(void)
+{
+ return parport_register_driver(&parkbd_parport_driver);
+}
+
+static void __exit parkbd_exit(void)
+{
+ parport_unregister_driver(&parkbd_parport_driver);
}
module_init(parkbd_init);
diff --git a/drivers/input/serio/userio.c b/drivers/input/serio/userio.c
new file mode 100644
index 000000000000..df1fd41860ac
--- /dev/null
+++ b/drivers/input/serio/userio.c
@@ -0,0 +1,285 @@
+/*
+ * userio kernel serio device emulation module
+ * Copyright (C) 2015 Red Hat
+ * Copyright (C) 2015 Stephen Chandler Paul <thatslyude@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser 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 Lesser
+ * General Public License for more details.
+ */
+
+#include <linux/circ_buf.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/serio.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <uapi/linux/userio.h>
+
+#define USERIO_NAME "userio"
+#define USERIO_BUFSIZE 16
+
+static struct miscdevice userio_misc;
+
+struct userio_device {
+ struct serio *serio;
+ struct mutex mutex;
+
+ bool running;
+
+ u8 head;
+ u8 tail;
+
+ spinlock_t buf_lock;
+ unsigned char buf[USERIO_BUFSIZE];
+
+ wait_queue_head_t waitq;
+};
+
+/**
+ * userio_device_write - Write data from serio to a userio device in userspace
+ * @id: The serio port for the userio device
+ * @val: The data to write to the device
+ */
+static int userio_device_write(struct serio *id, unsigned char val)
+{
+ struct userio_device *userio = id->port_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&userio->buf_lock, flags);
+
+ userio->buf[userio->head] = val;
+ userio->head = (userio->head + 1) % USERIO_BUFSIZE;
+
+ if (userio->head == userio->tail)
+ dev_warn(userio_misc.this_device,
+ "Buffer overflowed, userio client isn't keeping up");
+
+ spin_unlock_irqrestore(&userio->buf_lock, flags);
+
+ wake_up_interruptible(&userio->waitq);
+
+ return 0;
+}
+
+static int userio_char_open(struct inode *inode, struct file *file)
+{
+ struct userio_device *userio;
+
+ userio = kzalloc(sizeof(struct userio_device), GFP_KERNEL);
+ if (!userio)
+ return -ENOMEM;
+
+ mutex_init(&userio->mutex);
+ spin_lock_init(&userio->buf_lock);
+ init_waitqueue_head(&userio->waitq);
+
+ userio->serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!userio->serio) {
+ kfree(userio);
+ return -ENOMEM;
+ }
+
+ userio->serio->write = userio_device_write;
+ userio->serio->port_data = userio;
+
+ file->private_data = userio;
+
+ return 0;
+}
+
+static int userio_char_release(struct inode *inode, struct file *file)
+{
+ struct userio_device *userio = file->private_data;
+
+ if (userio->running) {
+ /*
+ * Don't free the serio port here, serio_unregister_port()
+ * does it for us.
+ */
+ serio_unregister_port(userio->serio);
+ } else {
+ kfree(userio->serio);
+ }
+
+ kfree(userio);
+
+ return 0;
+}
+
+static ssize_t userio_char_read(struct file *file, char __user *user_buffer,
+ size_t count, loff_t *ppos)
+{
+ struct userio_device *userio = file->private_data;
+ int error;
+ size_t nonwrap_len, copylen;
+ unsigned char buf[USERIO_BUFSIZE];
+ unsigned long flags;
+
+ /*
+ * By the time we get here, the data that was waiting might have
+ * been taken by another thread. Grab the buffer lock and check if
+ * there's still any data waiting, otherwise repeat this process
+ * until we have data (unless the file descriptor is non-blocking
+ * of course).
+ */
+ for (;;) {
+ spin_lock_irqsave(&userio->buf_lock, flags);
+
+ nonwrap_len = CIRC_CNT_TO_END(userio->head,
+ userio->tail,
+ USERIO_BUFSIZE);
+ copylen = min(nonwrap_len, count);
+ if (copylen) {
+ memcpy(buf, &userio->buf[userio->tail], copylen);
+ userio->tail = (userio->tail + copylen) %
+ USERIO_BUFSIZE;
+ }
+
+ spin_unlock_irqrestore(&userio->buf_lock, flags);
+
+ if (nonwrap_len)
+ break;
+
+ /* buffer was/is empty */
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ /*
+ * count == 0 is special - no IO is done but we check
+ * for error conditions (see above).
+ */
+ if (count == 0)
+ return 0;
+
+ error = wait_event_interruptible(userio->waitq,
+ userio->head != userio->tail);
+ if (error)
+ return error;
+ }
+
+ if (copylen)
+ if (copy_to_user(user_buffer, buf, copylen))
+ return -EFAULT;
+
+ return copylen;
+}
+
+static ssize_t userio_char_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct userio_device *userio = file->private_data;
+ struct userio_cmd cmd;
+ int error;
+
+ if (count != sizeof(cmd)) {
+ dev_warn(userio_misc.this_device, "Invalid payload size\n");
+ return -EINVAL;
+ }
+
+ if (copy_from_user(&cmd, buffer, sizeof(cmd)))
+ return -EFAULT;
+
+ error = mutex_lock_interruptible(&userio->mutex);
+ if (error)
+ return error;
+
+ switch (cmd.type) {
+ case USERIO_CMD_REGISTER:
+ if (!userio->serio->id.type) {
+ dev_warn(userio_misc.this_device,
+ "No port type given on /dev/userio\n");
+
+ error = -EINVAL;
+ goto out;
+ }
+
+ if (userio->running) {
+ dev_warn(userio_misc.this_device,
+ "Begin command sent, but we're already running\n");
+ error = -EBUSY;
+ goto out;
+ }
+
+ userio->running = true;
+ serio_register_port(userio->serio);
+ break;
+
+ case USERIO_CMD_SET_PORT_TYPE:
+ if (userio->running) {
+ dev_warn(userio_misc.this_device,
+ "Can't change port type on an already running userio instance\n");
+ error = -EBUSY;
+ goto out;
+ }
+
+ userio->serio->id.type = cmd.data;
+ break;
+
+ case USERIO_CMD_SEND_INTERRUPT:
+ if (!userio->running) {
+ dev_warn(userio_misc.this_device,
+ "The device must be registered before sending interrupts\n");
+ error = -ENODEV;
+ goto out;
+ }
+
+ serio_interrupt(userio->serio, cmd.data, 0);
+ break;
+
+ default:
+ error = -EOPNOTSUPP;
+ goto out;
+ }
+
+out:
+ mutex_unlock(&userio->mutex);
+ return error ?: count;
+}
+
+static unsigned int userio_char_poll(struct file *file, poll_table *wait)
+{
+ struct userio_device *userio = file->private_data;
+
+ poll_wait(file, &userio->waitq, wait);
+
+ if (userio->head != userio->tail)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static const struct file_operations userio_fops = {
+ .owner = THIS_MODULE,
+ .open = userio_char_open,
+ .release = userio_char_release,
+ .read = userio_char_read,
+ .write = userio_char_write,
+ .poll = userio_char_poll,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice userio_misc = {
+ .fops = &userio_fops,
+ .minor = USERIO_MINOR,
+ .name = USERIO_NAME,
+};
+module_driver(userio_misc, misc_register, misc_deregister);
+
+MODULE_ALIAS_MISCDEV(USERIO_MINOR);
+MODULE_ALIAS("devname:" USERIO_NAME);
+
+MODULE_AUTHOR("Stephen Chandler Paul <thatslyude@gmail.com>");
+MODULE_DESCRIPTION("Virtual Serio Device Support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index deb14c12ae8b..80cc69897a43 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -295,6 +295,19 @@ config TOUCHSCREEN_EGALAX
To compile this driver as a module, choose M here: the
module will be called egalax_ts.
+config TOUCHSCREEN_FT6236
+ tristate "FT6236 I2C touchscreen"
+ depends on I2C
+ depends on GPIOLIB || COMPILE_TEST
+ help
+ Say Y here to enable support for the I2C connected FT6x06 and
+ FT6x36 family of capacitive touchscreen drivers.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ft6236.
+
config TOUCHSCREEN_FUJITSU
tristate "Fujitsu serial touchscreen"
select SERIO
@@ -1065,4 +1078,15 @@ config TOUCHSCREEN_COLIBRI_VF50
To compile this driver as a module, choose M here: the
module will be called colibri_vf50_ts.
+config TOUCHSCREEN_ROHM_BU21023
+ tristate "ROHM BU21023/24 Dual touch support resistive touchscreens"
+ depends on I2C
+ help
+ Say Y here if you have a touchscreen using ROHM BU21023/24.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bu21023_ts.
+
endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 1b79cc09744a..17435c7e97e3 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o
+obj-$(CONFIG_TOUCHSCREEN_FT6236) += ft6236.o
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o
obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
@@ -87,3 +88,4 @@ obj-$(CONFIG_TOUCHSCREEN_SX8654) += sx8654.o
obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o
obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o
+obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index 04edc8f7122f..6c676e41fe3c 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -529,10 +529,8 @@ static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts)
ts->hwmon = hwmon_device_register_with_groups(&spi->dev, spi->modalias,
ts, ads7846_attr_groups);
- if (IS_ERR(ts->hwmon))
- return PTR_ERR(ts->hwmon);
- return 0;
+ return PTR_ERR_OR_ZERO(ts->hwmon);
}
static void ads784x_hwmon_unregister(struct spi_device *spi,
diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c
index 38c06f754acd..6592fc5d48b4 100644
--- a/drivers/input/touchscreen/auo-pixcir-ts.c
+++ b/drivers/input/touchscreen/auo-pixcir-ts.c
@@ -399,13 +399,8 @@ static int auo_pixcir_stop(struct auo_pixcir_ts *ts)
static int auo_pixcir_input_open(struct input_dev *dev)
{
struct auo_pixcir_ts *ts = input_get_drvdata(dev);
- int ret;
-
- ret = auo_pixcir_start(ts);
- if (ret)
- return ret;
- return 0;
+ return auo_pixcir_start(ts);
}
static void auo_pixcir_input_close(struct input_dev *dev)
diff --git a/drivers/input/touchscreen/cyttsp4_i2c.c b/drivers/input/touchscreen/cyttsp4_i2c.c
index a9f95c7d3c00..564e49002d5d 100644
--- a/drivers/input/touchscreen/cyttsp4_i2c.c
+++ b/drivers/input/touchscreen/cyttsp4_i2c.c
@@ -50,10 +50,7 @@ static int cyttsp4_i2c_probe(struct i2c_client *client,
ts = cyttsp4_probe(&cyttsp4_i2c_bus_ops, &client->dev, client->irq,
CYTTSP4_I2C_DATA_SIZE);
- if (IS_ERR(ts))
- return PTR_ERR(ts);
-
- return 0;
+ return PTR_ERR_OR_ZERO(ts);
}
static int cyttsp4_i2c_remove(struct i2c_client *client)
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index 48de1e8b3c93..0b0f8c17f3f7 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -27,6 +27,7 @@
#include <linux/module.h>
#include <linux/ratelimit.h>
+#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/i2c.h>
@@ -34,13 +35,10 @@
#include <linux/delay.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
-#include <linux/input/edt-ft5x06.h>
-
-#define MAX_SUPPORT_POINTS 5
+#include <linux/of_device.h>
#define WORK_REGISTER_THRESHOLD 0x00
#define WORK_REGISTER_REPORT_RATE 0x08
@@ -91,9 +89,8 @@ struct edt_ft5x06_ts_data {
u16 num_x;
u16 num_y;
- int reset_pin;
- int irq_pin;
- int wake_pin;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *wake_gpio;
#if defined(CONFIG_DEBUG_FS)
struct dentry *debug_dir;
@@ -107,6 +104,7 @@ struct edt_ft5x06_ts_data {
int gain;
int offset;
int report_rate;
+ int max_support_points;
char name[EDT_NAME_LEN];
@@ -114,6 +112,10 @@ struct edt_ft5x06_ts_data {
enum edt_ver version;
};
+struct edt_i2c_chip_data {
+ int max_support_points;
+};
+
static int edt_ft5x06_ts_readwrite(struct i2c_client *client,
u16 wr_len, u8 *wr_buf,
u16 rd_len, u8 *rd_buf)
@@ -170,9 +172,9 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
struct edt_ft5x06_ts_data *tsdata = dev_id;
struct device *dev = &tsdata->client->dev;
u8 cmd;
- u8 rdbuf[29];
+ u8 rdbuf[63];
int i, type, x, y, id;
- int offset, tplen, datalen;
+ int offset, tplen, datalen, crclen;
int error;
switch (tsdata->version) {
@@ -180,14 +182,14 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
cmd = 0xf9; /* tell the controller to send touch data */
offset = 5; /* where the actual touch data starts */
tplen = 4; /* data comes in so called frames */
- datalen = 26; /* how much bytes to listen for */
+ crclen = 1; /* length of the crc data */
break;
case M09:
- cmd = 0x02;
- offset = 1;
+ cmd = 0x0;
+ offset = 3;
tplen = 6;
- datalen = 29;
+ crclen = 0;
break;
default:
@@ -195,6 +197,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
}
memset(rdbuf, 0, sizeof(rdbuf));
+ datalen = tplen * tsdata->max_support_points + offset + crclen;
error = edt_ft5x06_ts_readwrite(tsdata->client,
sizeof(cmd), &cmd,
@@ -219,7 +222,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
goto out;
}
- for (i = 0; i < MAX_SUPPORT_POINTS; i++) {
+ for (i = 0; i < tsdata->max_support_points; i++) {
u8 *buf = &rdbuf[i * tplen + offset];
bool down;
@@ -752,45 +755,6 @@ edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
#endif /* CONFIG_DEBUGFS */
-static int edt_ft5x06_ts_reset(struct i2c_client *client,
- struct edt_ft5x06_ts_data *tsdata)
-{
- int error;
-
- if (gpio_is_valid(tsdata->wake_pin)) {
- error = devm_gpio_request_one(&client->dev,
- tsdata->wake_pin, GPIOF_OUT_INIT_LOW,
- "edt-ft5x06 wake");
- if (error) {
- dev_err(&client->dev,
- "Failed to request GPIO %d as wake pin, error %d\n",
- tsdata->wake_pin, error);
- return error;
- }
-
- msleep(5);
- gpio_set_value(tsdata->wake_pin, 1);
- }
- if (gpio_is_valid(tsdata->reset_pin)) {
- /* this pulls reset down, enabling the low active reset */
- error = devm_gpio_request_one(&client->dev,
- tsdata->reset_pin, GPIOF_OUT_INIT_LOW,
- "edt-ft5x06 reset");
- if (error) {
- dev_err(&client->dev,
- "Failed to request GPIO %d as reset pin, error %d\n",
- tsdata->reset_pin, error);
- return error;
- }
-
- msleep(5);
- gpio_set_value(tsdata->reset_pin, 1);
- msleep(300);
- }
-
- return 0;
-}
-
static int edt_ft5x06_ts_identify(struct i2c_client *client,
struct edt_ft5x06_ts_data *tsdata,
char *fw_version)
@@ -850,44 +814,24 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client,
return 0;
}
-#define EDT_ATTR_CHECKSET(name, reg) \
-do { \
- if (pdata->name >= edt_ft5x06_attr_##name.limit_low && \
- pdata->name <= edt_ft5x06_attr_##name.limit_high) \
- edt_ft5x06_register_write(tsdata, reg, pdata->name); \
-} while (0)
-
-#define EDT_GET_PROP(name, reg) { \
- u32 val; \
- if (of_property_read_u32(np, #name, &val) == 0) \
- edt_ft5x06_register_write(tsdata, reg, val); \
-}
-
-static void edt_ft5x06_ts_get_dt_defaults(struct device_node *np,
- struct edt_ft5x06_ts_data *tsdata)
+static void edt_ft5x06_ts_get_defaults(struct device *dev,
+ struct edt_ft5x06_ts_data *tsdata)
{
struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
+ u32 val;
+ int error;
- EDT_GET_PROP(threshold, reg_addr->reg_threshold);
- EDT_GET_PROP(gain, reg_addr->reg_gain);
- EDT_GET_PROP(offset, reg_addr->reg_offset);
-}
+ error = device_property_read_u32(dev, "threshold", &val);
+ if (!error)
+ reg_addr->reg_threshold = val;
-static void
-edt_ft5x06_ts_get_defaults(struct edt_ft5x06_ts_data *tsdata,
- const struct edt_ft5x06_platform_data *pdata)
-{
- struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
-
- if (!pdata->use_parameters)
- return;
+ error = device_property_read_u32(dev, "gain", &val);
+ if (!error)
+ reg_addr->reg_gain = val;
- /* pick up defaults from the platform data */
- EDT_ATTR_CHECKSET(threshold, reg_addr->reg_threshold);
- EDT_ATTR_CHECKSET(gain, reg_addr->reg_gain);
- EDT_ATTR_CHECKSET(offset, reg_addr->reg_offset);
- if (reg_addr->reg_report_rate != NO_REGISTER)
- EDT_ATTR_CHECKSET(report_rate, reg_addr->reg_report_rate);
+ error = device_property_read_u32(dev, "offset", &val);
+ if (!error)
+ reg_addr->reg_offset = val;
}
static void
@@ -931,37 +875,13 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata)
}
}
-#ifdef CONFIG_OF
-static int edt_ft5x06_i2c_ts_probe_dt(struct device *dev,
- struct edt_ft5x06_ts_data *tsdata)
-{
- struct device_node *np = dev->of_node;
-
- /*
- * irq_pin is not needed for DT setup.
- * irq is associated via 'interrupts' property in DT
- */
- tsdata->irq_pin = -EINVAL;
- tsdata->reset_pin = of_get_named_gpio(np, "reset-gpios", 0);
- tsdata->wake_pin = of_get_named_gpio(np, "wake-gpios", 0);
-
- return 0;
-}
-#else
-static inline int edt_ft5x06_i2c_ts_probe_dt(struct device *dev,
- struct edt_ft5x06_ts_data *tsdata)
-{
- return -ENODEV;
-}
-#endif
-
static int edt_ft5x06_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- const struct edt_ft5x06_platform_data *pdata =
- dev_get_platdata(&client->dev);
+ const struct edt_i2c_chip_data *chip_data;
struct edt_ft5x06_ts_data *tsdata;
struct input_dev *input;
+ unsigned long irq_flags;
int error;
char fw_version[EDT_NAME_LEN];
@@ -973,32 +893,43 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
return -ENOMEM;
}
- if (!pdata) {
- error = edt_ft5x06_i2c_ts_probe_dt(&client->dev, tsdata);
- if (error) {
- dev_err(&client->dev,
- "DT probe failed and no platform data present\n");
- return error;
- }
- } else {
- tsdata->reset_pin = pdata->reset_pin;
- tsdata->irq_pin = pdata->irq_pin;
- tsdata->wake_pin = -EINVAL;
+ chip_data = of_device_get_match_data(&client->dev);
+ if (!chip_data)
+ chip_data = (const struct edt_i2c_chip_data *)id->driver_data;
+ if (!chip_data || !chip_data->max_support_points) {
+ dev_err(&client->dev, "invalid or missing chip data\n");
+ return -EINVAL;
}
- error = edt_ft5x06_ts_reset(client, tsdata);
- if (error)
+ tsdata->max_support_points = chip_data->max_support_points;
+
+ tsdata->reset_gpio = devm_gpiod_get_optional(&client->dev,
+ "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(tsdata->reset_gpio)) {
+ error = PTR_ERR(tsdata->reset_gpio);
+ dev_err(&client->dev,
+ "Failed to request GPIO reset pin, error %d\n", error);
+ return error;
+ }
+
+ tsdata->wake_gpio = devm_gpiod_get_optional(&client->dev,
+ "wake", GPIOD_OUT_LOW);
+ if (IS_ERR(tsdata->wake_gpio)) {
+ error = PTR_ERR(tsdata->wake_gpio);
+ dev_err(&client->dev,
+ "Failed to request GPIO wake pin, error %d\n", error);
return error;
+ }
- if (gpio_is_valid(tsdata->irq_pin)) {
- error = devm_gpio_request_one(&client->dev, tsdata->irq_pin,
- GPIOF_IN, "edt-ft5x06 irq");
- if (error) {
- dev_err(&client->dev,
- "Failed to request GPIO %d, error %d\n",
- tsdata->irq_pin, error);
- return error;
- }
+ if (tsdata->wake_gpio) {
+ usleep_range(5000, 6000);
+ gpiod_set_value_cansleep(tsdata->wake_gpio, 1);
+ }
+
+ if (tsdata->reset_gpio) {
+ usleep_range(5000, 6000);
+ gpiod_set_value_cansleep(tsdata->reset_gpio, 0);
+ msleep(300);
}
input = devm_input_allocate_device(&client->dev);
@@ -1019,12 +950,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
}
edt_ft5x06_ts_set_regs(tsdata);
-
- if (!pdata)
- edt_ft5x06_ts_get_dt_defaults(client->dev.of_node, tsdata);
- else
- edt_ft5x06_ts_get_defaults(tsdata, pdata);
-
+ edt_ft5x06_ts_get_defaults(&client->dev, tsdata);
edt_ft5x06_ts_get_parameters(tsdata);
dev_dbg(&client->dev,
@@ -1040,10 +966,10 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
input_set_abs_params(input, ABS_MT_POSITION_Y,
0, tsdata->num_y * 64 - 1, 0, 0);
- if (!pdata)
- touchscreen_parse_properties(input, true);
+ touchscreen_parse_properties(input, true);
- error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, INPUT_MT_DIRECT);
+ error = input_mt_init_slots(input, tsdata->max_support_points,
+ INPUT_MT_DIRECT);
if (error) {
dev_err(&client->dev, "Unable to init MT slots.\n");
return error;
@@ -1052,9 +978,13 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
input_set_drvdata(input, tsdata);
i2c_set_clientdata(client, tsdata);
- error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
- edt_ft5x06_ts_isr,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ irq_flags = irq_get_trigger_type(client->irq);
+ if (irq_flags == IRQF_TRIGGER_NONE)
+ irq_flags = IRQF_TRIGGER_FALLING;
+ irq_flags |= IRQF_ONESHOT;
+
+ error = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, edt_ft5x06_ts_isr, irq_flags,
client->name, tsdata);
if (error) {
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
@@ -1074,7 +1004,9 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
dev_dbg(&client->dev,
"EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n",
- client->irq, tsdata->wake_pin, tsdata->reset_pin);
+ client->irq,
+ tsdata->wake_gpio ? desc_to_gpio(tsdata->wake_gpio) : -1,
+ tsdata->reset_gpio ? desc_to_gpio(tsdata->reset_gpio) : -1);
return 0;
@@ -1116,17 +1048,27 @@ static int __maybe_unused edt_ft5x06_ts_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops,
edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume);
+static const struct edt_i2c_chip_data edt_ft5x06_data = {
+ .max_support_points = 5,
+};
+
+static const struct edt_i2c_chip_data edt_ft5506_data = {
+ .max_support_points = 10,
+};
+
static const struct i2c_device_id edt_ft5x06_ts_id[] = {
- { "edt-ft5x06", 0, },
+ { .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data },
+ { .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id);
#ifdef CONFIG_OF
static const struct of_device_id edt_ft5x06_of_match[] = {
- { .compatible = "edt,edt-ft5206", },
- { .compatible = "edt,edt-ft5306", },
- { .compatible = "edt,edt-ft5406", },
+ { .compatible = "edt,edt-ft5206", .data = &edt_ft5x06_data },
+ { .compatible = "edt,edt-ft5306", .data = &edt_ft5x06_data },
+ { .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data },
+ { .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match);
diff --git a/drivers/input/touchscreen/ft6236.c b/drivers/input/touchscreen/ft6236.c
new file mode 100644
index 000000000000..d240d2e212bd
--- /dev/null
+++ b/drivers/input/touchscreen/ft6236.c
@@ -0,0 +1,326 @@
+/*
+ * FocalTech FT6236 TouchScreen driver.
+ *
+ * Copyright (c) 2010 Focal tech Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/property.h>
+
+#define FT6236_MAX_TOUCH_POINTS 2
+
+#define FT6236_REG_TH_GROUP 0x80
+#define FT6236_REG_PERIODACTIVE 0x88
+#define FT6236_REG_LIB_VER_H 0xa1
+#define FT6236_REG_LIB_VER_L 0xa2
+#define FT6236_REG_CIPHER 0xa3
+#define FT6236_REG_FIRMID 0xa6
+#define FT6236_REG_FOCALTECH_ID 0xa8
+#define FT6236_REG_RELEASE_CODE_ID 0xaf
+
+#define FT6236_EVENT_PRESS_DOWN 0
+#define FT6236_EVENT_LIFT_UP 1
+#define FT6236_EVENT_CONTACT 2
+#define FT6236_EVENT_NO_EVENT 3
+
+struct ft6236_data {
+ struct i2c_client *client;
+ struct input_dev *input;
+ struct gpio_desc *reset_gpio;
+ u32 max_x;
+ u32 max_y;
+ bool invert_x;
+ bool invert_y;
+ bool swap_xy;
+};
+
+/*
+ * This struct is a touchpoint as stored in hardware. Note that the id,
+ * as well as the event, are stored in the upper nybble of the hi byte.
+ */
+struct ft6236_touchpoint {
+ union {
+ u8 xhi;
+ u8 event;
+ };
+ u8 xlo;
+ union {
+ u8 yhi;
+ u8 id;
+ };
+ u8 ylo;
+ u8 weight;
+ u8 misc;
+} __packed;
+
+/* This packet represents the register map as read from offset 0 */
+struct ft6236_packet {
+ u8 dev_mode;
+ u8 gest_id;
+ u8 touches;
+ struct ft6236_touchpoint points[FT6236_MAX_TOUCH_POINTS];
+} __packed;
+
+static int ft6236_read(struct i2c_client *client, u8 reg, u8 len, void *data)
+{
+ int error;
+
+ error = i2c_smbus_read_i2c_block_data(client, reg, len, data);
+ if (error < 0)
+ return error;
+
+ if (error != len)
+ return -EIO;
+
+ return 0;
+}
+
+static irqreturn_t ft6236_interrupt(int irq, void *dev_id)
+{
+ struct ft6236_data *ft6236 = dev_id;
+ struct device *dev = &ft6236->client->dev;
+ struct input_dev *input = ft6236->input;
+ struct ft6236_packet buf;
+ u8 touches;
+ int i, error;
+
+ error = ft6236_read(ft6236->client, 0, sizeof(buf), &buf);
+ if (error) {
+ dev_err(dev, "read touchdata failed %d\n", error);
+ return IRQ_HANDLED;
+ }
+
+ touches = buf.touches & 0xf;
+ if (touches > FT6236_MAX_TOUCH_POINTS) {
+ dev_dbg(dev,
+ "%d touch points reported, only %d are supported\n",
+ touches, FT6236_MAX_TOUCH_POINTS);
+ touches = FT6236_MAX_TOUCH_POINTS;
+ }
+
+ for (i = 0; i < touches; i++) {
+ struct ft6236_touchpoint *point = &buf.points[i];
+ u16 x = ((point->xhi & 0xf) << 8) | buf.points[i].xlo;
+ u16 y = ((point->yhi & 0xf) << 8) | buf.points[i].ylo;
+ u8 event = point->event >> 6;
+ u8 id = point->id >> 4;
+ bool act = (event == FT6236_EVENT_PRESS_DOWN ||
+ event == FT6236_EVENT_CONTACT);
+
+ input_mt_slot(input, id);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, act);
+ if (!act)
+ continue;
+
+ if (ft6236->invert_x)
+ x = ft6236->max_x - x;
+
+ if (ft6236->invert_y)
+ y = ft6236->max_y - y;
+
+ if (ft6236->swap_xy) {
+ input_report_abs(input, ABS_MT_POSITION_X, y);
+ input_report_abs(input, ABS_MT_POSITION_Y, x);
+ } else {
+ input_report_abs(input, ABS_MT_POSITION_X, x);
+ input_report_abs(input, ABS_MT_POSITION_Y, y);
+ }
+ }
+
+ input_mt_sync_frame(input);
+ input_sync(input);
+
+ return IRQ_HANDLED;
+}
+
+static u8 ft6236_debug_read_byte(struct ft6236_data *ft6236, u8 reg)
+{
+ struct i2c_client *client = ft6236->client;
+ u8 val = 0;
+ int error;
+
+ error = ft6236_read(client, reg, 1, &val);
+ if (error)
+ dev_dbg(&client->dev,
+ "error reading register 0x%02x: %d\n", reg, error);
+
+ return val;
+}
+
+static void ft6236_debug_info(struct ft6236_data *ft6236)
+{
+ struct device *dev = &ft6236->client->dev;
+
+ dev_dbg(dev, "Touch threshold is %d\n",
+ ft6236_debug_read_byte(ft6236, FT6236_REG_TH_GROUP) * 4);
+ dev_dbg(dev, "Report rate is %dHz\n",
+ ft6236_debug_read_byte(ft6236, FT6236_REG_PERIODACTIVE) * 10);
+ dev_dbg(dev, "Firmware library version 0x%02x%02x\n",
+ ft6236_debug_read_byte(ft6236, FT6236_REG_LIB_VER_H),
+ ft6236_debug_read_byte(ft6236, FT6236_REG_LIB_VER_L));
+ dev_dbg(dev, "Firmware version 0x%02x\n",
+ ft6236_debug_read_byte(ft6236, FT6236_REG_FIRMID));
+ dev_dbg(dev, "Chip vendor ID 0x%02x\n",
+ ft6236_debug_read_byte(ft6236, FT6236_REG_CIPHER));
+ dev_dbg(dev, "CTPM vendor ID 0x%02x\n",
+ ft6236_debug_read_byte(ft6236, FT6236_REG_FOCALTECH_ID));
+ dev_dbg(dev, "Release code version 0x%02x\n",
+ ft6236_debug_read_byte(ft6236, FT6236_REG_RELEASE_CODE_ID));
+}
+
+static void ft6236_reset(struct ft6236_data *ft6236)
+{
+ if (!ft6236->reset_gpio)
+ return;
+
+ gpiod_set_value_cansleep(ft6236->reset_gpio, 1);
+ usleep_range(5000, 20000);
+ gpiod_set_value_cansleep(ft6236->reset_gpio, 0);
+ msleep(300);
+}
+
+static int ft6236_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct ft6236_data *ft6236;
+ struct input_dev *input;
+ u32 fuzz_x = 0, fuzz_y = 0;
+ u8 val;
+ int error;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENXIO;
+
+ if (!client->irq) {
+ dev_err(dev, "irq is missing\n");
+ return -EINVAL;
+ }
+
+ ft6236 = devm_kzalloc(dev, sizeof(*ft6236), GFP_KERNEL);
+ if (!ft6236)
+ return -ENOMEM;
+
+ ft6236->client = client;
+ ft6236->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ft6236->reset_gpio)) {
+ error = PTR_ERR(ft6236->reset_gpio);
+ if (error != -EPROBE_DEFER)
+ dev_err(dev, "error getting reset gpio: %d\n", error);
+ return error;
+ }
+
+ ft6236_reset(ft6236);
+
+ /* verify that the controller is present */
+ error = ft6236_read(client, 0x00, 1, &val);
+ if (error) {
+ dev_err(dev, "failed to read from controller: %d\n", error);
+ return error;
+ }
+
+ ft6236_debug_info(ft6236);
+
+ input = devm_input_allocate_device(dev);
+ if (!input)
+ return -ENOMEM;
+
+ ft6236->input = input;
+ input->name = client->name;
+ input->id.bustype = BUS_I2C;
+
+ if (device_property_read_u32(dev, "touchscreen-size-x",
+ &ft6236->max_x) ||
+ device_property_read_u32(dev, "touchscreen-size-y",
+ &ft6236->max_y)) {
+ dev_err(dev, "touchscreen-size-x and/or -y missing\n");
+ return -EINVAL;
+ }
+
+ device_property_read_u32(dev, "touchscreen-fuzz-x", &fuzz_x);
+ device_property_read_u32(dev, "touchscreen-fuzz-y", &fuzz_y);
+ ft6236->invert_x = device_property_read_bool(dev,
+ "touchscreen-inverted-x");
+ ft6236->invert_y = device_property_read_bool(dev,
+ "touchscreen-inverted-y");
+ ft6236->swap_xy = device_property_read_bool(dev,
+ "touchscreen-swapped-x-y");
+
+ if (ft6236->swap_xy) {
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0,
+ ft6236->max_y, fuzz_y, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
+ ft6236->max_x, fuzz_x, 0);
+ } else {
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0,
+ ft6236->max_x, fuzz_x, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
+ ft6236->max_y, fuzz_y, 0);
+ }
+
+ error = input_mt_init_slots(input, FT6236_MAX_TOUCH_POINTS,
+ INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+ if (error)
+ return error;
+
+ error = devm_request_threaded_irq(dev, client->irq, NULL,
+ ft6236_interrupt, IRQF_ONESHOT,
+ client->name, ft6236);
+ if (error) {
+ dev_err(dev, "request irq %d failed: %d\n", client->irq, error);
+ return error;
+ }
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(dev, "failed to register input device: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id ft6236_of_match[] = {
+ { .compatible = "focaltech,ft6236", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ft6236_of_match);
+#endif
+
+static const struct i2c_device_id ft6236_id[] = {
+ { "ft6236", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ft6236_id);
+
+static struct i2c_driver ft6236_driver = {
+ .driver = {
+ .name = "ft6236",
+ .of_match_table = of_match_ptr(ft6236_of_match),
+ },
+ .probe = ft6236_probe,
+ .id_table = ft6236_id,
+};
+module_i2c_driver(ft6236_driver);
+
+MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
+MODULE_AUTHOR("Noralf Trønnes <noralf@tronnes.org>");
+MODULE_DESCRIPTION("FocalTech FT6236 TouchScreen driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c
index cf0dc2f0b1be..ddf694b9fffc 100644
--- a/drivers/input/touchscreen/ili210x.c
+++ b/drivers/input/touchscreen/ili210x.c
@@ -216,7 +216,7 @@ static int ili210x_i2c_probe(struct i2c_client *client,
/* get panel info */
error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel));
if (error) {
- dev_err(dev, "Failed to get panel informations, err: %d\n",
+ dev_err(dev, "Failed to get panel information, err: %d\n",
error);
return error;
}
@@ -276,7 +276,7 @@ static int ili210x_i2c_probe(struct i2c_client *client,
error = input_register_device(priv->input);
if (error) {
- dev_err(dev, "Cannot regiser input device, err: %d\n", error);
+ dev_err(dev, "Cannot register input device, err: %d\n", error);
goto err_remove_sysfs;
}
diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
index 91621725bfb5..4b961ad9f0b5 100644
--- a/drivers/input/touchscreen/pixcir_i2c_ts.c
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -377,8 +377,6 @@ static int __maybe_unused pixcir_i2c_ts_suspend(struct device *dev)
goto unlock;
}
}
-
- enable_irq_wake(client->irq);
} else if (input->users) {
ret = pixcir_stop(ts);
}
@@ -399,7 +397,6 @@ static int __maybe_unused pixcir_i2c_ts_resume(struct device *dev)
mutex_lock(&input->mutex);
if (device_may_wakeup(&client->dev)) {
- disable_irq_wake(client->irq);
if (!input->users) {
ret = pixcir_stop(ts);
@@ -564,14 +561,6 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
return error;
i2c_set_clientdata(client, tsdata);
- device_init_wakeup(&client->dev, 1);
-
- return 0;
-}
-
-static int pixcir_i2c_ts_remove(struct i2c_client *client)
-{
- device_init_wakeup(&client->dev, 0);
return 0;
}
@@ -609,7 +598,6 @@ static struct i2c_driver pixcir_i2c_ts_driver = {
.of_match_table = of_match_ptr(pixcir_of_match),
},
.probe = pixcir_i2c_ts_probe,
- .remove = pixcir_i2c_ts_remove,
.id_table = pixcir_i2c_ts_id,
};
diff --git a/drivers/input/touchscreen/rohm_bu21023.c b/drivers/input/touchscreen/rohm_bu21023.c
new file mode 100644
index 000000000000..ba6024f93469
--- /dev/null
+++ b/drivers/input/touchscreen/rohm_bu21023.c
@@ -0,0 +1,1218 @@
+/*
+ * ROHM BU21023/24 Dual touch support resistive touch screen driver
+ * Copyright (C) 2012 ROHM CO.,LTD.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#define BU21023_NAME "bu21023_ts"
+#define BU21023_FIRMWARE_NAME "bu21023.bin"
+
+#define MAX_CONTACTS 2
+
+#define AXIS_ADJUST 4
+#define AXIS_OFFSET 8
+
+#define FIRMWARE_BLOCK_SIZE 32U
+#define FIRMWARE_RETRY_MAX 4
+
+#define SAMPLING_DELAY 12 /* msec */
+
+#define CALIBRATION_RETRY_MAX 6
+
+#define ROHM_TS_ABS_X_MIN 40
+#define ROHM_TS_ABS_X_MAX 990
+#define ROHM_TS_ABS_Y_MIN 160
+#define ROHM_TS_ABS_Y_MAX 920
+#define ROHM_TS_DISPLACEMENT_MAX 0 /* zero for infinite */
+
+/*
+ * BU21023GUL/BU21023MUV/BU21024FV-M registers map
+ */
+#define VADOUT_YP_H 0x00
+#define VADOUT_YP_L 0x01
+#define VADOUT_XP_H 0x02
+#define VADOUT_XP_L 0x03
+#define VADOUT_YN_H 0x04
+#define VADOUT_YN_L 0x05
+#define VADOUT_XN_H 0x06
+#define VADOUT_XN_L 0x07
+
+#define PRM1_X_H 0x08
+#define PRM1_X_L 0x09
+#define PRM1_Y_H 0x0a
+#define PRM1_Y_L 0x0b
+#define PRM2_X_H 0x0c
+#define PRM2_X_L 0x0d
+#define PRM2_Y_H 0x0e
+#define PRM2_Y_L 0x0f
+
+#define MLT_PRM_MONI_X 0x10
+#define MLT_PRM_MONI_Y 0x11
+
+#define DEBUG_MONI_1 0x12
+#define DEBUG_MONI_2 0x13
+
+#define VADOUT_ZX_H 0x14
+#define VADOUT_ZX_L 0x15
+#define VADOUT_ZY_H 0x16
+#define VADOUT_ZY_L 0x17
+
+#define Z_PARAM_H 0x18
+#define Z_PARAM_L 0x19
+
+/*
+ * Value for VADOUT_*_L
+ */
+#define VADOUT_L_MASK 0x01
+
+/*
+ * Value for PRM*_*_L
+ */
+#define PRM_L_MASK 0x01
+
+#define POS_X1_H 0x20
+#define POS_X1_L 0x21
+#define POS_Y1_H 0x22
+#define POS_Y1_L 0x23
+#define POS_X2_H 0x24
+#define POS_X2_L 0x25
+#define POS_Y2_H 0x26
+#define POS_Y2_L 0x27
+
+/*
+ * Value for POS_*_L
+ */
+#define POS_L_MASK 0x01
+
+#define TOUCH 0x28
+#define TOUCH_DETECT 0x01
+
+#define TOUCH_GESTURE 0x29
+#define SINGLE_TOUCH 0x01
+#define DUAL_TOUCH 0x03
+#define TOUCH_MASK 0x03
+#define CALIBRATION_REQUEST 0x04
+#define CALIBRATION_STATUS 0x08
+#define CALIBRATION_MASK 0x0c
+#define GESTURE_SPREAD 0x10
+#define GESTURE_PINCH 0x20
+#define GESTURE_ROTATE_R 0x40
+#define GESTURE_ROTATE_L 0x80
+
+#define INT_STATUS 0x2a
+#define INT_MASK 0x3d
+#define INT_CLEAR 0x3e
+
+/*
+ * Values for INT_*
+ */
+#define COORD_UPDATE 0x01
+#define CALIBRATION_DONE 0x02
+#define SLEEP_IN 0x04
+#define SLEEP_OUT 0x08
+#define PROGRAM_LOAD_DONE 0x10
+#define ERROR 0x80
+#define INT_ALL 0x9f
+
+#define ERR_STATUS 0x2b
+#define ERR_MASK 0x3f
+
+/*
+ * Values for ERR_*
+ */
+#define ADC_TIMEOUT 0x01
+#define CPU_TIMEOUT 0x02
+#define CALIBRATION_ERR 0x04
+#define PROGRAM_LOAD_ERR 0x10
+
+#define COMMON_SETUP1 0x30
+#define PROGRAM_LOAD_HOST 0x02
+#define PROGRAM_LOAD_EEPROM 0x03
+#define CENSOR_4PORT 0x04
+#define CENSOR_8PORT 0x00 /* Not supported by BU21023 */
+#define CALIBRATION_TYPE_DEFAULT 0x08
+#define CALIBRATION_TYPE_SPECIAL 0x00
+#define INT_ACTIVE_HIGH 0x10
+#define INT_ACTIVE_LOW 0x00
+#define AUTO_CALIBRATION 0x40
+#define MANUAL_CALIBRATION 0x00
+#define COMMON_SETUP1_DEFAULT 0x4e
+
+#define COMMON_SETUP2 0x31
+#define MAF_NONE 0x00
+#define MAF_1SAMPLE 0x01
+#define MAF_3SAMPLES 0x02
+#define MAF_5SAMPLES 0x03
+#define INV_Y 0x04
+#define INV_X 0x08
+#define SWAP_XY 0x10
+
+#define COMMON_SETUP3 0x32
+#define EN_SLEEP 0x01
+#define EN_MULTI 0x02
+#define EN_GESTURE 0x04
+#define EN_INTVL 0x08
+#define SEL_STEP 0x10
+#define SEL_MULTI 0x20
+#define SEL_TBL_DEFAULT 0x40
+
+#define INTERVAL_TIME 0x33
+#define INTERVAL_TIME_DEFAULT 0x10
+
+#define STEP_X 0x34
+#define STEP_X_DEFAULT 0x41
+
+#define STEP_Y 0x35
+#define STEP_Y_DEFAULT 0x8d
+
+#define OFFSET_X 0x38
+#define OFFSET_X_DEFAULT 0x0c
+
+#define OFFSET_Y 0x39
+#define OFFSET_Y_DEFAULT 0x0c
+
+#define THRESHOLD_TOUCH 0x3a
+#define THRESHOLD_TOUCH_DEFAULT 0xa0
+
+#define THRESHOLD_GESTURE 0x3b
+#define THRESHOLD_GESTURE_DEFAULT 0x17
+
+#define SYSTEM 0x40
+#define ANALOG_POWER_ON 0x01
+#define ANALOG_POWER_OFF 0x00
+#define CPU_POWER_ON 0x02
+#define CPU_POWER_OFF 0x00
+
+#define FORCE_CALIBRATION 0x42
+#define FORCE_CALIBRATION_ON 0x01
+#define FORCE_CALIBRATION_OFF 0x00
+
+#define CPU_FREQ 0x50 /* 10 / (reg + 1) MHz */
+#define CPU_FREQ_10MHZ 0x00
+#define CPU_FREQ_5MHZ 0x01
+#define CPU_FREQ_1MHZ 0x09
+
+#define EEPROM_ADDR 0x51
+
+#define CALIBRATION_ADJUST 0x52
+#define CALIBRATION_ADJUST_DEFAULT 0x00
+
+#define THRESHOLD_SLEEP_IN 0x53
+
+#define EVR_XY 0x56
+#define EVR_XY_DEFAULT 0x10
+
+#define PRM_SWOFF_TIME 0x57
+#define PRM_SWOFF_TIME_DEFAULT 0x04
+
+#define PROGRAM_VERSION 0x5f
+
+#define ADC_CTRL 0x60
+#define ADC_DIV_MASK 0x1f /* The minimum value is 4 */
+#define ADC_DIV_DEFAULT 0x08
+
+#define ADC_WAIT 0x61
+#define ADC_WAIT_DEFAULT 0x0a
+
+#define SWCONT 0x62
+#define SWCONT_DEFAULT 0x0f
+
+#define EVR_X 0x63
+#define EVR_X_DEFAULT 0x86
+
+#define EVR_Y 0x64
+#define EVR_Y_DEFAULT 0x64
+
+#define TEST1 0x65
+#define DUALTOUCH_STABILIZE_ON 0x01
+#define DUALTOUCH_STABILIZE_OFF 0x00
+#define DUALTOUCH_REG_ON 0x20
+#define DUALTOUCH_REG_OFF 0x00
+
+#define CALIBRATION_REG1 0x68
+#define CALIBRATION_REG1_DEFAULT 0xd9
+
+#define CALIBRATION_REG2 0x69
+#define CALIBRATION_REG2_DEFAULT 0x36
+
+#define CALIBRATION_REG3 0x6a
+#define CALIBRATION_REG3_DEFAULT 0x32
+
+#define EX_ADDR_H 0x70
+#define EX_ADDR_L 0x71
+#define EX_WDAT 0x72
+#define EX_RDAT 0x73
+#define EX_CHK_SUM1 0x74
+#define EX_CHK_SUM2 0x75
+#define EX_CHK_SUM3 0x76
+
+struct rohm_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input;
+
+ bool initialized;
+
+ unsigned int contact_count[MAX_CONTACTS + 1];
+ int finger_count;
+
+ u8 setup2;
+};
+
+/*
+ * rohm_i2c_burst_read - execute combined I2C message for ROHM BU21023/24
+ * @client: Handle to ROHM BU21023/24
+ * @start: Where to start read address from ROHM BU21023/24
+ * @buf: Where to store read data from ROHM BU21023/24
+ * @len: How many bytes to read
+ *
+ * Returns negative errno, else zero on success.
+ *
+ * Note
+ * In BU21023/24 burst read, stop condition is needed after "address write".
+ * Therefore, transmission is performed in 2 steps.
+ */
+static int rohm_i2c_burst_read(struct i2c_client *client, u8 start, void *buf,
+ size_t len)
+{
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_msg msg[2];
+ int i, ret = 0;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = &start;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = len;
+ msg[1].buf = buf;
+
+ i2c_lock_adapter(adap);
+
+ for (i = 0; i < 2; i++) {
+ if (__i2c_transfer(adap, &msg[i], 1) < 0) {
+ ret = -EIO;
+ break;
+ }
+ }
+
+ i2c_unlock_adapter(adap);
+
+ return ret;
+}
+
+static int rohm_ts_manual_calibration(struct rohm_ts_data *ts)
+{
+ struct i2c_client *client = ts->client;
+ struct device *dev = &client->dev;
+ u8 buf[33]; /* for PRM1_X_H(0x08)-TOUCH(0x28) */
+
+ int retry;
+ bool success = false;
+ bool first_time = true;
+ bool calibration_done;
+
+ u8 reg1, reg2, reg3;
+ s32 reg1_orig, reg2_orig, reg3_orig;
+ s32 val;
+
+ int calib_x = 0, calib_y = 0;
+ int reg_x, reg_y;
+ int err_x, err_y;
+
+ int error, error2;
+ int i;
+
+ reg1_orig = i2c_smbus_read_byte_data(client, CALIBRATION_REG1);
+ if (reg1_orig < 0)
+ return reg1_orig;
+
+ reg2_orig = i2c_smbus_read_byte_data(client, CALIBRATION_REG2);
+ if (reg2_orig < 0)
+ return reg2_orig;
+
+ reg3_orig = i2c_smbus_read_byte_data(client, CALIBRATION_REG3);
+ if (reg3_orig < 0)
+ return reg3_orig;
+
+ error = i2c_smbus_write_byte_data(client, INT_MASK,
+ COORD_UPDATE | SLEEP_IN | SLEEP_OUT |
+ PROGRAM_LOAD_DONE);
+ if (error)
+ goto out;
+
+ error = i2c_smbus_write_byte_data(client, TEST1,
+ DUALTOUCH_STABILIZE_ON);
+ if (error)
+ goto out;
+
+ for (retry = 0; retry < CALIBRATION_RETRY_MAX; retry++) {
+ /* wait 2 sampling for update */
+ mdelay(2 * SAMPLING_DELAY);
+
+#define READ_CALIB_BUF(reg) buf[((reg) - PRM1_X_H)]
+
+ error = rohm_i2c_burst_read(client, PRM1_X_H, buf, sizeof(buf));
+ if (error)
+ goto out;
+
+ if (READ_CALIB_BUF(TOUCH) & TOUCH_DETECT)
+ continue;
+
+ if (first_time) {
+ /* generate calibration parameter */
+ calib_x = ((int)READ_CALIB_BUF(PRM1_X_H) << 2 |
+ READ_CALIB_BUF(PRM1_X_L)) - AXIS_OFFSET;
+ calib_y = ((int)READ_CALIB_BUF(PRM1_Y_H) << 2 |
+ READ_CALIB_BUF(PRM1_Y_L)) - AXIS_OFFSET;
+
+ error = i2c_smbus_write_byte_data(client, TEST1,
+ DUALTOUCH_STABILIZE_ON | DUALTOUCH_REG_ON);
+ if (error)
+ goto out;
+
+ first_time = false;
+ } else {
+ /* generate adjustment parameter */
+ err_x = (int)READ_CALIB_BUF(PRM1_X_H) << 2 |
+ READ_CALIB_BUF(PRM1_X_L);
+ err_y = (int)READ_CALIB_BUF(PRM1_Y_H) << 2 |
+ READ_CALIB_BUF(PRM1_Y_L);
+
+ /* X axis ajust */
+ if (err_x <= 4)
+ calib_x -= AXIS_ADJUST;
+ else if (err_x >= 60)
+ calib_x += AXIS_ADJUST;
+
+ /* Y axis ajust */
+ if (err_y <= 4)
+ calib_y -= AXIS_ADJUST;
+ else if (err_y >= 60)
+ calib_y += AXIS_ADJUST;
+ }
+
+ /* generate calibration setting value */
+ reg_x = calib_x + ((calib_x & 0x200) << 1);
+ reg_y = calib_y + ((calib_y & 0x200) << 1);
+
+ /* convert for register format */
+ reg1 = reg_x >> 3;
+ reg2 = (reg_y & 0x7) << 4 | (reg_x & 0x7);
+ reg3 = reg_y >> 3;
+
+ error = i2c_smbus_write_byte_data(client,
+ CALIBRATION_REG1, reg1);
+ if (error)
+ goto out;
+
+ error = i2c_smbus_write_byte_data(client,
+ CALIBRATION_REG2, reg2);
+ if (error)
+ goto out;
+
+ error = i2c_smbus_write_byte_data(client,
+ CALIBRATION_REG3, reg3);
+ if (error)
+ goto out;
+
+ /*
+ * force calibration sequcence
+ */
+ error = i2c_smbus_write_byte_data(client, FORCE_CALIBRATION,
+ FORCE_CALIBRATION_OFF);
+ if (error)
+ goto out;
+
+ error = i2c_smbus_write_byte_data(client, FORCE_CALIBRATION,
+ FORCE_CALIBRATION_ON);
+ if (error)
+ goto out;
+
+ /* clear all interrupts */
+ error = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+ if (error)
+ goto out;
+
+ /*
+ * Wait for the status change of calibration, max 10 sampling
+ */
+ calibration_done = false;
+
+ for (i = 0; i < 10; i++) {
+ mdelay(SAMPLING_DELAY);
+
+ val = i2c_smbus_read_byte_data(client, TOUCH_GESTURE);
+ if (!(val & CALIBRATION_MASK)) {
+ calibration_done = true;
+ break;
+ } else if (val < 0) {
+ error = val;
+ goto out;
+ }
+ }
+
+ if (calibration_done) {
+ val = i2c_smbus_read_byte_data(client, INT_STATUS);
+ if (val == CALIBRATION_DONE) {
+ success = true;
+ break;
+ } else if (val < 0) {
+ error = val;
+ goto out;
+ }
+ } else {
+ dev_warn(dev, "calibration timeout\n");
+ }
+ }
+
+ if (!success) {
+ error = i2c_smbus_write_byte_data(client, CALIBRATION_REG1,
+ reg1_orig);
+ if (error)
+ goto out;
+
+ error = i2c_smbus_write_byte_data(client, CALIBRATION_REG2,
+ reg2_orig);
+ if (error)
+ goto out;
+
+ error = i2c_smbus_write_byte_data(client, CALIBRATION_REG3,
+ reg3_orig);
+ if (error)
+ goto out;
+
+ /* calibration data enable */
+ error = i2c_smbus_write_byte_data(client, TEST1,
+ DUALTOUCH_STABILIZE_ON |
+ DUALTOUCH_REG_ON);
+ if (error)
+ goto out;
+
+ /* wait 10 sampling */
+ mdelay(10 * SAMPLING_DELAY);
+
+ error = -EBUSY;
+ }
+
+out:
+ error2 = i2c_smbus_write_byte_data(client, INT_MASK, INT_ALL);
+ if (!error2)
+ /* Clear all interrupts */
+ error2 = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+
+ return error ? error : error2;
+}
+
+static const unsigned int untouch_threshold[3] = { 0, 1, 5 };
+static const unsigned int single_touch_threshold[3] = { 0, 0, 4 };
+static const unsigned int dual_touch_threshold[3] = { 10, 8, 0 };
+
+static irqreturn_t rohm_ts_soft_irq(int irq, void *dev_id)
+{
+ struct rohm_ts_data *ts = dev_id;
+ struct i2c_client *client = ts->client;
+ struct input_dev *input_dev = ts->input;
+ struct device *dev = &client->dev;
+
+ u8 buf[10]; /* for POS_X1_H(0x20)-TOUCH_GESTURE(0x29) */
+
+ struct input_mt_pos pos[MAX_CONTACTS];
+ int slots[MAX_CONTACTS];
+ u8 touch_flags;
+ unsigned int threshold;
+ int finger_count = -1;
+ int prev_finger_count = ts->finger_count;
+ int count;
+ int error;
+ int i;
+
+ error = i2c_smbus_write_byte_data(client, INT_MASK, INT_ALL);
+ if (error)
+ return IRQ_HANDLED;
+
+ /* Clear all interrupts */
+ error = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+ if (error)
+ return IRQ_HANDLED;
+
+#define READ_POS_BUF(reg) buf[((reg) - POS_X1_H)]
+
+ error = rohm_i2c_burst_read(client, POS_X1_H, buf, sizeof(buf));
+ if (error)
+ return IRQ_HANDLED;
+
+ touch_flags = READ_POS_BUF(TOUCH_GESTURE) & TOUCH_MASK;
+ if (touch_flags) {
+ /* generate coordinates */
+ pos[0].x = ((s16)READ_POS_BUF(POS_X1_H) << 2) |
+ READ_POS_BUF(POS_X1_L);
+ pos[0].y = ((s16)READ_POS_BUF(POS_Y1_H) << 2) |
+ READ_POS_BUF(POS_Y1_L);
+ pos[1].x = ((s16)READ_POS_BUF(POS_X2_H) << 2) |
+ READ_POS_BUF(POS_X2_L);
+ pos[1].y = ((s16)READ_POS_BUF(POS_Y2_H) << 2) |
+ READ_POS_BUF(POS_Y2_L);
+ }
+
+ switch (touch_flags) {
+ case 0:
+ threshold = untouch_threshold[prev_finger_count];
+ if (++ts->contact_count[0] >= threshold)
+ finger_count = 0;
+ break;
+
+ case SINGLE_TOUCH:
+ threshold = single_touch_threshold[prev_finger_count];
+ if (++ts->contact_count[1] >= threshold)
+ finger_count = 1;
+
+ if (finger_count == 1) {
+ if (pos[1].x != 0 && pos[1].y != 0) {
+ pos[0].x = pos[1].x;
+ pos[0].y = pos[1].y;
+ pos[1].x = 0;
+ pos[1].y = 0;
+ }
+ }
+ break;
+
+ case DUAL_TOUCH:
+ threshold = dual_touch_threshold[prev_finger_count];
+ if (++ts->contact_count[2] >= threshold)
+ finger_count = 2;
+ break;
+
+ default:
+ dev_dbg(dev,
+ "Three or more touches are not supported\n");
+ return IRQ_HANDLED;
+ }
+
+ if (finger_count >= 0) {
+ if (prev_finger_count != finger_count) {
+ count = ts->contact_count[finger_count];
+ memset(ts->contact_count, 0, sizeof(ts->contact_count));
+ ts->contact_count[finger_count] = count;
+ }
+
+ input_mt_assign_slots(input_dev, slots, pos,
+ finger_count, ROHM_TS_DISPLACEMENT_MAX);
+
+ for (i = 0; i < finger_count; i++) {
+ input_mt_slot(input_dev, slots[i]);
+ input_mt_report_slot_state(input_dev,
+ MT_TOOL_FINGER, true);
+ input_report_abs(input_dev,
+ ABS_MT_POSITION_X, pos[i].x);
+ input_report_abs(input_dev,
+ ABS_MT_POSITION_Y, pos[i].y);
+ }
+
+ input_mt_sync_frame(input_dev);
+ input_mt_report_pointer_emulation(input_dev, true);
+ input_sync(input_dev);
+
+ ts->finger_count = finger_count;
+ }
+
+ if (READ_POS_BUF(TOUCH_GESTURE) & CALIBRATION_REQUEST) {
+ error = rohm_ts_manual_calibration(ts);
+ if (error)
+ dev_warn(dev, "manual calibration failed: %d\n",
+ error);
+ }
+
+ i2c_smbus_write_byte_data(client, INT_MASK,
+ CALIBRATION_DONE | SLEEP_OUT | SLEEP_IN |
+ PROGRAM_LOAD_DONE);
+
+ return IRQ_HANDLED;
+}
+
+static int rohm_ts_load_firmware(struct i2c_client *client,
+ const char *firmware_name)
+{
+ struct device *dev = &client->dev;
+ const struct firmware *fw;
+ s32 status;
+ unsigned int offset, len, xfer_len;
+ unsigned int retry = 0;
+ int error, error2;
+
+ error = request_firmware(&fw, firmware_name, dev);
+ if (error) {
+ dev_err(dev, "unable to retrieve firmware %s: %d\n",
+ firmware_name, error);
+ return error;
+ }
+
+ error = i2c_smbus_write_byte_data(client, INT_MASK,
+ COORD_UPDATE | CALIBRATION_DONE |
+ SLEEP_IN | SLEEP_OUT);
+ if (error)
+ goto out;
+
+ do {
+ if (retry) {
+ dev_warn(dev, "retrying firmware load\n");
+
+ /* settings for retry */
+ error = i2c_smbus_write_byte_data(client, EX_WDAT, 0);
+ if (error)
+ goto out;
+ }
+
+ error = i2c_smbus_write_byte_data(client, EX_ADDR_H, 0);
+ if (error)
+ goto out;
+
+ error = i2c_smbus_write_byte_data(client, EX_ADDR_L, 0);
+ if (error)
+ goto out;
+
+ error = i2c_smbus_write_byte_data(client, COMMON_SETUP1,
+ COMMON_SETUP1_DEFAULT);
+ if (error)
+ goto out;
+
+ /* firmware load to the device */
+ offset = 0;
+ len = fw->size;
+
+ while (len) {
+ xfer_len = min(FIRMWARE_BLOCK_SIZE, len);
+
+ error = i2c_smbus_write_i2c_block_data(client, EX_WDAT,
+ xfer_len, &fw->data[offset]);
+ if (error)
+ goto out;
+
+ len -= xfer_len;
+ offset += xfer_len;
+ }
+
+ /* check firmware load result */
+ status = i2c_smbus_read_byte_data(client, INT_STATUS);
+ if (status < 0) {
+ error = status;
+ goto out;
+ }
+
+ /* clear all interrupts */
+ error = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+ if (error)
+ goto out;
+
+ if (status == PROGRAM_LOAD_DONE)
+ break;
+
+ error = -EIO;
+ } while (++retry >= FIRMWARE_RETRY_MAX);
+
+out:
+ error2 = i2c_smbus_write_byte_data(client, INT_MASK, INT_ALL);
+
+ release_firmware(fw);
+
+ return error ? error : error2;
+}
+
+static ssize_t swap_xy_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct rohm_ts_data *ts = i2c_get_clientdata(client);
+
+ return sprintf(buf, "%d\n", !!(ts->setup2 & SWAP_XY));
+}
+
+static ssize_t swap_xy_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct rohm_ts_data *ts = i2c_get_clientdata(client);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 0, &val);
+ if (error)
+ return error;
+
+ error = mutex_lock_interruptible(&ts->input->mutex);
+ if (error)
+ return error;
+
+ if (val)
+ ts->setup2 |= SWAP_XY;
+ else
+ ts->setup2 &= ~SWAP_XY;
+
+ if (ts->initialized)
+ error = i2c_smbus_write_byte_data(ts->client, COMMON_SETUP2,
+ ts->setup2);
+
+ mutex_unlock(&ts->input->mutex);
+
+ return error ? error : count;
+}
+
+static ssize_t inv_x_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct rohm_ts_data *ts = i2c_get_clientdata(client);
+
+ return sprintf(buf, "%d\n", !!(ts->setup2 & INV_X));
+}
+
+static ssize_t inv_x_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct rohm_ts_data *ts = i2c_get_clientdata(client);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 0, &val);
+ if (error)
+ return error;
+
+ error = mutex_lock_interruptible(&ts->input->mutex);
+ if (error)
+ return error;
+
+ if (val)
+ ts->setup2 |= INV_X;
+ else
+ ts->setup2 &= ~INV_X;
+
+ if (ts->initialized)
+ error = i2c_smbus_write_byte_data(ts->client, COMMON_SETUP2,
+ ts->setup2);
+
+ mutex_unlock(&ts->input->mutex);
+
+ return error ? error : count;
+}
+
+static ssize_t inv_y_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct rohm_ts_data *ts = i2c_get_clientdata(client);
+
+ return sprintf(buf, "%d\n", !!(ts->setup2 & INV_Y));
+}
+
+static ssize_t inv_y_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct rohm_ts_data *ts = i2c_get_clientdata(client);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 0, &val);
+ if (error)
+ return error;
+
+ error = mutex_lock_interruptible(&ts->input->mutex);
+ if (error)
+ return error;
+
+ if (val)
+ ts->setup2 |= INV_Y;
+ else
+ ts->setup2 &= ~INV_Y;
+
+ if (ts->initialized)
+ error = i2c_smbus_write_byte_data(client, COMMON_SETUP2,
+ ts->setup2);
+
+ mutex_unlock(&ts->input->mutex);
+
+ return error ? error : count;
+}
+
+static DEVICE_ATTR_RW(swap_xy);
+static DEVICE_ATTR_RW(inv_x);
+static DEVICE_ATTR_RW(inv_y);
+
+static struct attribute *rohm_ts_attrs[] = {
+ &dev_attr_swap_xy.attr,
+ &dev_attr_inv_x.attr,
+ &dev_attr_inv_y.attr,
+ NULL,
+};
+
+static const struct attribute_group rohm_ts_attr_group = {
+ .attrs = rohm_ts_attrs,
+};
+
+static int rohm_ts_device_init(struct i2c_client *client, u8 setup2)
+{
+ struct device *dev = &client->dev;
+ int error;
+
+ disable_irq(client->irq);
+
+ /*
+ * Wait 200usec for reset
+ */
+ udelay(200);
+
+ /* Release analog reset */
+ error = i2c_smbus_write_byte_data(client, SYSTEM,
+ ANALOG_POWER_ON | CPU_POWER_OFF);
+ if (error)
+ return error;
+
+ /* Waiting for the analog warm-up, max. 200usec */
+ udelay(200);
+
+ /* clear all interrupts */
+ error = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, EX_WDAT, 0);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, COMMON_SETUP1, 0);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, COMMON_SETUP2, setup2);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, COMMON_SETUP3,
+ SEL_TBL_DEFAULT | EN_MULTI);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, THRESHOLD_GESTURE,
+ THRESHOLD_GESTURE_DEFAULT);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, INTERVAL_TIME,
+ INTERVAL_TIME_DEFAULT);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, CPU_FREQ, CPU_FREQ_10MHZ);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, PRM_SWOFF_TIME,
+ PRM_SWOFF_TIME_DEFAULT);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, ADC_CTRL, ADC_DIV_DEFAULT);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, ADC_WAIT, ADC_WAIT_DEFAULT);
+ if (error)
+ return error;
+
+ /*
+ * Panel setup, these values change with the panel.
+ */
+ error = i2c_smbus_write_byte_data(client, STEP_X, STEP_X_DEFAULT);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, STEP_Y, STEP_Y_DEFAULT);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, OFFSET_X, OFFSET_X_DEFAULT);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, OFFSET_Y, OFFSET_Y_DEFAULT);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, THRESHOLD_TOUCH,
+ THRESHOLD_TOUCH_DEFAULT);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, EVR_XY, EVR_XY_DEFAULT);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, EVR_X, EVR_X_DEFAULT);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, EVR_Y, EVR_Y_DEFAULT);
+ if (error)
+ return error;
+
+ /* Fixed value settings */
+ error = i2c_smbus_write_byte_data(client, CALIBRATION_ADJUST,
+ CALIBRATION_ADJUST_DEFAULT);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, SWCONT, SWCONT_DEFAULT);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, TEST1,
+ DUALTOUCH_STABILIZE_ON |
+ DUALTOUCH_REG_ON);
+ if (error)
+ return error;
+
+ error = rohm_ts_load_firmware(client, BU21023_FIRMWARE_NAME);
+ if (error) {
+ dev_err(dev, "failed to load firmware: %d\n", error);
+ return error;
+ }
+
+ /*
+ * Manual calibration results are not changed in same environment.
+ * If the force calibration is performed,
+ * the controller will not require calibration request interrupt
+ * when the typical values are set to the calibration registers.
+ */
+ error = i2c_smbus_write_byte_data(client, CALIBRATION_REG1,
+ CALIBRATION_REG1_DEFAULT);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, CALIBRATION_REG2,
+ CALIBRATION_REG2_DEFAULT);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, CALIBRATION_REG3,
+ CALIBRATION_REG3_DEFAULT);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, FORCE_CALIBRATION,
+ FORCE_CALIBRATION_OFF);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, FORCE_CALIBRATION,
+ FORCE_CALIBRATION_ON);
+ if (error)
+ return error;
+
+ /* Clear all interrupts */
+ error = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+ if (error)
+ return error;
+
+ /* Enable coordinates update interrupt */
+ error = i2c_smbus_write_byte_data(client, INT_MASK,
+ CALIBRATION_DONE | SLEEP_OUT |
+ SLEEP_IN | PROGRAM_LOAD_DONE);
+ if (error)
+ return error;
+
+ error = i2c_smbus_write_byte_data(client, ERR_MASK,
+ PROGRAM_LOAD_ERR | CPU_TIMEOUT |
+ ADC_TIMEOUT);
+ if (error)
+ return error;
+
+ /* controller CPU power on */
+ error = i2c_smbus_write_byte_data(client, SYSTEM,
+ ANALOG_POWER_ON | CPU_POWER_ON);
+
+ enable_irq(client->irq);
+
+ return error;
+}
+
+static int rohm_ts_power_off(struct i2c_client *client)
+{
+ int error;
+
+ error = i2c_smbus_write_byte_data(client, SYSTEM,
+ ANALOG_POWER_ON | CPU_POWER_OFF);
+ if (error) {
+ dev_err(&client->dev,
+ "failed to power off device CPU: %d\n", error);
+ return error;
+ }
+
+ error = i2c_smbus_write_byte_data(client, SYSTEM,
+ ANALOG_POWER_OFF | CPU_POWER_OFF);
+ if (error)
+ dev_err(&client->dev,
+ "failed to power off the device: %d\n", error);
+
+ return error;
+}
+
+static int rohm_ts_open(struct input_dev *input_dev)
+{
+ struct rohm_ts_data *ts = input_get_drvdata(input_dev);
+ struct i2c_client *client = ts->client;
+ int error;
+
+ if (!ts->initialized) {
+ error = rohm_ts_device_init(client, ts->setup2);
+ if (error) {
+ dev_err(&client->dev,
+ "device initialization failed: %d\n", error);
+ return error;
+ }
+
+ ts->initialized = true;
+ }
+
+ return 0;
+}
+
+static void rohm_ts_close(struct input_dev *input_dev)
+{
+ struct rohm_ts_data *ts = input_get_drvdata(input_dev);
+
+ rohm_ts_power_off(ts->client);
+
+ ts->initialized = false;
+}
+
+static void rohm_ts_remove_sysfs_group(void *_dev)
+{
+ struct device *dev = _dev;
+
+ sysfs_remove_group(&dev->kobj, &rohm_ts_attr_group);
+}
+
+static int rohm_bu21023_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct rohm_ts_data *ts;
+ struct input_dev *input;
+ int error;
+
+ if (!client->irq) {
+ dev_err(dev, "IRQ is not assigned\n");
+ return -EINVAL;
+ }
+
+ if (!client->adapter->algo->master_xfer) {
+ dev_err(dev, "I2C level transfers not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ /* Turn off CPU just in case */
+ error = rohm_ts_power_off(client);
+ if (error)
+ return error;
+
+ ts = devm_kzalloc(dev, sizeof(struct rohm_ts_data), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ ts->client = client;
+ ts->setup2 = MAF_1SAMPLE;
+ i2c_set_clientdata(client, ts);
+
+ input = devm_input_allocate_device(dev);
+ if (!input)
+ return -ENOMEM;
+
+ input->name = BU21023_NAME;
+ input->id.bustype = BUS_I2C;
+ input->open = rohm_ts_open;
+ input->close = rohm_ts_close;
+
+ ts->input = input;
+ input_set_drvdata(input, ts);
+
+ input_set_abs_params(input, ABS_MT_POSITION_X,
+ ROHM_TS_ABS_X_MIN, ROHM_TS_ABS_X_MAX, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y,
+ ROHM_TS_ABS_Y_MIN, ROHM_TS_ABS_Y_MAX, 0, 0);
+
+ error = input_mt_init_slots(input, MAX_CONTACTS,
+ INPUT_MT_DIRECT | INPUT_MT_TRACK |
+ INPUT_MT_DROP_UNUSED);
+ if (error) {
+ dev_err(dev, "failed to multi touch slots initialization\n");
+ return error;
+ }
+
+ error = devm_request_threaded_irq(dev, client->irq,
+ NULL, rohm_ts_soft_irq,
+ IRQF_ONESHOT, client->name, ts);
+ if (error) {
+ dev_err(dev, "failed to request IRQ: %d\n", error);
+ return error;
+ }
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(dev, "failed to register input device: %d\n", error);
+ return error;
+ }
+
+ error = sysfs_create_group(&dev->kobj, &rohm_ts_attr_group);
+ if (error) {
+ dev_err(dev, "failed to create sysfs group: %d\n", error);
+ return error;
+ }
+
+ error = devm_add_action(dev, rohm_ts_remove_sysfs_group, dev);
+ if (error) {
+ rohm_ts_remove_sysfs_group(dev);
+ dev_err(&client->dev,
+ "Failed to add sysfs cleanup action: %d\n",
+ error);
+ return error;
+ }
+
+ return error;
+}
+
+static const struct i2c_device_id rohm_bu21023_i2c_id[] = {
+ { BU21023_NAME, 0 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, rohm_bu21023_i2c_id);
+
+static struct i2c_driver rohm_bu21023_i2c_driver = {
+ .driver = {
+ .name = BU21023_NAME,
+ },
+ .probe = rohm_bu21023_i2c_probe,
+ .id_table = rohm_bu21023_i2c_id,
+};
+module_i2c_driver(rohm_bu21023_i2c_driver);
+
+MODULE_DESCRIPTION("ROHM BU21023/24 Touchscreen driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("ROHM Co., Ltd.");
diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c
index c0116994067d..485794376ee5 100644
--- a/drivers/input/touchscreen/sun4i-ts.c
+++ b/drivers/input/touchscreen/sun4i-ts.c
@@ -191,7 +191,7 @@ static void sun4i_ts_close(struct input_dev *dev)
writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
}
-static int sun4i_get_temp(const struct sun4i_ts_data *ts, long *temp)
+static int sun4i_get_temp(const struct sun4i_ts_data *ts, int *temp)
{
/* No temp_data until the first irq */
if (ts->temp_data == -1)
@@ -202,7 +202,7 @@ static int sun4i_get_temp(const struct sun4i_ts_data *ts, long *temp)
return 0;
}
-static int sun4i_get_tz_temp(void *data, long *temp)
+static int sun4i_get_tz_temp(void *data, int *temp)
{
return sun4i_get_temp(data, temp);
}
@@ -215,14 +215,14 @@ 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 temp;
int error;
error = sun4i_get_temp(ts, &temp);
if (error)
return error;
- return sprintf(buf, "%ld\n", temp);
+ return sprintf(buf, "%d\n", temp);
}
static ssize_t show_temp_label(struct device *dev,
diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c
index 4ffd829d1990..a340bfccdfb6 100644
--- a/drivers/input/touchscreen/tps6507x-ts.c
+++ b/drivers/input/touchscreen/tps6507x-ts.c
@@ -50,14 +50,7 @@ struct tps6507x_ts {
static int tps6507x_read_u8(struct tps6507x_ts *tsc, u8 reg, u8 *data)
{
- int err;
-
- err = tsc->mfd->read_dev(tsc->mfd, reg, 1, data);
-
- if (err)
- return err;
-
- return 0;
+ return tsc->mfd->read_dev(tsc->mfd, reg, 1, data);
}
static int tps6507x_write_u8(struct tps6507x_ts *tsc, u8 reg, u8 data)
diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c
index b1ae77995968..1534e9b0788c 100644
--- a/drivers/input/touchscreen/wm97xx-core.c
+++ b/drivers/input/touchscreen/wm97xx-core.c
@@ -732,8 +732,7 @@ static int wm97xx_remove(struct device *dev)
return 0;
}
-#ifdef CONFIG_PM
-static int wm97xx_suspend(struct device *dev, pm_message_t state)
+static int __maybe_unused wm97xx_suspend(struct device *dev)
{
struct wm97xx *wm = dev_get_drvdata(dev);
u16 reg;
@@ -765,7 +764,7 @@ static int wm97xx_suspend(struct device *dev, pm_message_t state)
return 0;
}
-static int wm97xx_resume(struct device *dev)
+static int __maybe_unused wm97xx_resume(struct device *dev)
{
struct wm97xx *wm = dev_get_drvdata(dev);
@@ -799,10 +798,7 @@ static int wm97xx_resume(struct device *dev)
return 0;
}
-#else
-#define wm97xx_suspend NULL
-#define wm97xx_resume NULL
-#endif
+static SIMPLE_DEV_PM_OPS(wm97xx_pm_ops, wm97xx_suspend, wm97xx_resume);
/*
* Machine specific operations
@@ -836,8 +832,7 @@ static struct device_driver wm97xx_driver = {
.owner = THIS_MODULE,
.probe = wm97xx_probe,
.remove = wm97xx_remove,
- .suspend = wm97xx_suspend,
- .resume = wm97xx_resume,
+ .pm = &wm97xx_pm_ops,
};
static int __init wm97xx_init(void)
diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c
index 781d0f83050a..9bbadaaf6bc3 100644
--- a/drivers/input/touchscreen/zforce_ts.c
+++ b/drivers/input/touchscreen/zforce_ts.c
@@ -599,13 +599,8 @@ static irqreturn_t zforce_irq_thread(int irq, void *dev_id)
static int zforce_input_open(struct input_dev *dev)
{
struct zforce_ts *ts = input_get_drvdata(dev);
- int ret;
- ret = zforce_start(ts);
- if (ret)
- return ret;
-
- return 0;
+ return zforce_start(ts);
}
static void zforce_input_close(struct input_dev *dev)