* [PATCH 2/2] input: egalax: report end state in suspend callback
From: Felipe F. Tonello @ 2013-12-31 2:06 UTC (permalink / raw)
To: linux-input
Cc: Felipe F. Tonello, linux-kernel, Henrik Rydberg, Dmitry Torokhov
In-Reply-To: <1388455601-17033-1-git-send-email-eu@felipetonello.com>
From: "Felipe F. Tonello" <eu@felipetonello.com>
Make sure user-space will receive the touch end event even if the device
driver suspends while touchscreen is still active (with fingers touching it).
Signed-off-by: Felipe F. Tonello <eu@felipetonello.com>
---
drivers/input/touchscreen/egalax_ts.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c
index 054d225..9cf4cdb 100644
--- a/drivers/input/touchscreen/egalax_ts.c
+++ b/drivers/input/touchscreen/egalax_ts.c
@@ -247,8 +247,13 @@ static int egalax_ts_suspend(struct device *dev)
0x3, 0x6, 0xa, 0x3, 0x36, 0x3f, 0x2, 0, 0, 0
};
struct i2c_client *client = to_i2c_client(dev);
+ struct egalax_ts *ts = i2c_get_clientdata(client);
int ret;
+ input_mt_report_end_state(ts->input_dev);
+ input_mt_report_pointer_emulation(ts->input_dev, true);
+ input_sync(ts->input_dev);
+
ret = i2c_master_send(client, suspend_cmd, MAX_I2C_DATA_LEN);
return ret > 0 ? 0 : ret;
}
--
1.8.3.1
^ permalink raw reply related
* [PATCH 0/2] input: mt: Add helper function to send end events
From: Felipe F. Tonello @ 2013-12-31 2:06 UTC (permalink / raw)
To: linux-input
Cc: Felipe F. Tonello, linux-kernel, Henrik Rydberg, Dmitry Torokhov
From: "Felipe F. Tonello" <eu@felipetonello.com>
Adds a helper function to send end touch events for active tracking ids.
And also implements it in egalax driver.
Felipe F. Tonello (2):
input: mt: Add helper function to send end events
input: egalax: report end state in suspend callback
drivers/input/input-mt.c | 33 +++++++++++++++++++++++++++++++++
drivers/input/touchscreen/egalax_ts.c | 5 +++++
include/linux/input/mt.h | 2 ++
3 files changed, 40 insertions(+)
--
1.8.3.1
^ permalink raw reply
* [PATCH 1/2] input: mt: Add helper function to send end events
From: Felipe F. Tonello @ 2013-12-31 2:06 UTC (permalink / raw)
To: linux-input
Cc: Felipe F. Tonello, linux-kernel, Henrik Rydberg, Dmitry Torokhov
In-Reply-To: <1388455601-17033-1-git-send-email-eu@felipetonello.com>
From: "Felipe F. Tonello" <eu@felipetonello.com>
This is useful to report for users of devices that don't know anything
about the suspension of the device. So users will receive a touch end event
when the device is about to suspend, making it more user friendly.
One example of users is the X Server with the evdev input driver. This patch
make sure that the X server will propagate a touch end event to its windows.
Signed-off-by: Felipe F. Tonello <eu@felipetonello.com>
---
drivers/input/input-mt.c | 33 +++++++++++++++++++++++++++++++++
include/linux/input/mt.h | 2 ++
2 files changed, 35 insertions(+)
diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c
index d398f13..6010357 100644
--- a/drivers/input/input-mt.c
+++ b/drivers/input/input-mt.c
@@ -157,6 +157,39 @@ void input_mt_report_slot_state(struct input_dev *dev,
EXPORT_SYMBOL(input_mt_report_slot_state);
/**
+ * input_mt_report_end_state() - report end touch events
+ * @dev: input device with allocated MT slots
+ *
+ * Reports a touch end event for current active slots (with active tracking id).
+ * This is useful when the device might suspend (or sleep) while there were
+ * still active tracking ids.
+ */
+void input_mt_report_end_state(struct input_dev *dev)
+{
+ struct input_mt *mt = dev->mt;
+ struct input_mt_slot *slot;
+ int id, i;
+
+ if (!mt)
+ return;
+
+ for (i = 0; i < mt->num_slots; ++i) {
+ slot = &mt->slots[i];
+ slot->frame = mt->frame;
+
+ id = input_mt_get_value(slot, ABS_MT_TRACKING_ID);
+
+ /* if id == -1 is 'unused' */
+ if (id >= 0) {
+ input_mt_set_value(slot, ABS_MT_TRACKING_ID, -1);
+ input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ }
+ }
+}
+EXPORT_SYMBOL(input_mt_report_end_state);
+
+
+/**
* input_mt_report_finger_count() - report contact count
* @dev: input device with allocated MT slots
* @count: the number of contacts
diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h
index 1b1dfa8..0e6c9f7 100644
--- a/include/linux/input/mt.h
+++ b/include/linux/input/mt.h
@@ -103,6 +103,8 @@ static inline bool input_is_mt_axis(int axis)
void input_mt_report_slot_state(struct input_dev *dev,
unsigned int tool_type, bool active);
+void input_mt_report_end_state(struct input_dev *dev);
+
void input_mt_report_finger_count(struct input_dev *dev, int count);
void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count);
--
1.8.3.1
^ permalink raw reply related
* [PATCH] input synaptics-rmi4 trivial: rmi_driver.c tidy-up
From: Christopher Heiny @ 2013-12-31 2:54 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
Vivian Ly, Daniel Rosenberg, Jean Delvare, Joerie de Gram,
Linus Walleij, Benjamin Tissoires
This has three trivial changes:
1) multi-line commenting is changed to kernel coding standard
2) reset_and_reflash() function is renamed to rmi_initial_reset() and
recommentedto reflect what it actually does.
3) #endif comment was updated to actuall match the opening #if CONFIG_PM_SLEEP
condition.
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
drivers/input/rmi4/rmi_driver.c | 35 ++++++++++++++++++++---------------
1 file changed, 20 insertions(+), 15 deletions(-)
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index a4e5236..82ae30c 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -77,8 +77,10 @@ static void rmi_poll_work(struct work_struct *work)
process_interrupt_requests(rmi_dev);
}
-/* This is the timer function for polling - it simply has to schedule work
- * and restart the timer. */
+/*
+ * This is the timer function for polling - it simply has to schedule work
+ * and restart the timer.
+ */
static enum hrtimer_restart rmi_poll_timer(struct hrtimer *timer)
{
struct rmi_driver_data *data =
@@ -295,12 +297,14 @@ static int process_interrupt_requests(struct rmi_device *rmi_dev)
mutex_lock(&data->irq_mutex);
bitmap_and(data->irq_status, data->irq_status, data->current_irq_mask,
data->irq_count);
- /* At this point, irq_status has all bits that are set in the
+ /*
+ * At this point, irq_status has all bits that are set in the
* interrupt status register and are enabled.
*/
mutex_unlock(&data->irq_mutex);
- /* It would be nice to be able to use irq_chip to handle these
+ /*
+ * It would be nice to be able to use irq_chip to handle these
* nested IRQs. Unfortunately, most of the current customers for
* this driver are using older kernels (3.0.x) that don't support
* the features required for that. Once they've shifted to more
@@ -418,7 +422,8 @@ static int rmi_driver_irq_handler(struct rmi_device *rmi_dev, int irq)
struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
might_sleep();
- /* Can get called before the driver is fully ready to deal with
+ /*
+ * Can get called before the driver is fully ready to deal with
* interrupts.
*/
if (!data || !data->f01_container) {
@@ -435,7 +440,8 @@ static int rmi_driver_reset_handler(struct rmi_device *rmi_dev)
struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
int error;
- /* Can get called before the driver is fully ready to deal with
+ /*
+ * Can get called before the driver is fully ready to deal with
* this situation.
*/
if (!data || !data->f01_container) {
@@ -571,13 +577,10 @@ err_free_mem:
* forces application of any pending updates from reflashing the
* firmware or configuration.
*
- * At this time, we also reflash the device if (a) in kernel reflashing is
- * enabled, and (b) the reflash module decides it requires reflashing.
- *
* We have to do this before actually building the PDT because the reflash
- * might cause various registers to move around.
+ * updates (if any) might cause various registers to move around.
*/
-static int reset_and_reflash(struct rmi_device *rmi_dev)
+static int rmi_initial_reset(struct rmi_device *rmi_dev)
{
struct pdt_entry pdt_entry;
int page;
@@ -745,7 +748,7 @@ exit:
return retval;
}
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(rmi_driver_pm, rmi_driver_suspend, rmi_driver_resume);
@@ -791,7 +794,8 @@ static int rmi_driver_probe(struct device *dev)
dev_set_drvdata(&rmi_dev->dev, data);
mutex_init(&data->pdt_mutex);
- /* Right before a warm boot, the sensor might be in some unusual state,
+ /*
+ * Right before a warm boot, the sensor might be in some unusual state,
* such as F54 diagnostics, or F34 bootloader mode. In order to clear
* the sensor to a known state, we issue a initial reset to clear any
* previous settings and force it into normal operation.
@@ -808,7 +812,7 @@ static int rmi_driver_probe(struct device *dev)
*/
if (!pdata->reset_delay_ms)
pdata->reset_delay_ms = DEFAULT_RESET_DELAY_MS;
- retval = reset_and_reflash(rmi_dev);
+ retval = rmi_initial_reset(rmi_dev);
if (retval)
dev_warn(dev, "RMI initial reset failed! Continuing in spite of this.\n");
@@ -836,7 +840,8 @@ static int rmi_driver_probe(struct device *dev)
retval = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION, &data->pdt_props);
if (retval < 0) {
- /* we'll print out a warning and continue since
+ /*
+ * we'll print out a warning and continue since
* failure to get the PDT properties is not a cause to fail
*/
dev_warn(dev, "Could not read PDT properties from %#06x. Assuming 0x00.\n",
^ permalink raw reply related
* [PATCH] input synaptics-rmi4 trivial: rmi_f01.c tidy-up
From: Christopher Heiny @ 2013-12-31 3:10 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
Vivian Ly, Daniel Rosenberg, Jean Delvare, Joerie de Gram,
Linus Walleij, Benjamin Tissoires
This has two trivial changes:
1) make sure spacing around operators is correct.
2) capitalize Synaptics appropriately.
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
drivers/input/rmi4/rmi_f01.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c
index 840a8d0..628b082 100644
--- a/drivers/input/rmi4/rmi_f01.c
+++ b/drivers/input/rmi4/rmi_f01.c
@@ -156,7 +156,7 @@ static int rmi_f01_alloc_memory(struct rmi_function *fn,
}
f01->device_control.interrupt_enable = devm_kzalloc(&fn->dev,
- sizeof(u8)*(num_of_irq_regs),
+ sizeof(u8) * (num_of_irq_regs),
GFP_KERNEL);
if (!f01->device_control.interrupt_enable) {
dev_err(&fn->dev, "Failed to allocate interrupt enable.\n");
@@ -280,7 +280,7 @@ static int rmi_f01_initialize(struct rmi_function *fn)
dev_info(&fn->dev, "found RMI device, manufacturer: %s, product: %s\n",
data->properties.manufacturer_id == 1 ?
- "synaptics" : "unknown",
+ "Synaptics" : "unknown",
data->properties.product_id);
/* read control register */
^ permalink raw reply related
* Re: [PATCH 0/2] input: mt: Add helper function to send end events
From: Henrik Rydberg @ 2013-12-31 9:26 UTC (permalink / raw)
To: Felipe F. Tonello, linux-input; +Cc: linux-kernel, Dmitry Torokhov
In-Reply-To: <1388455601-17033-1-git-send-email-eu@felipetonello.com>
Hi Felipe,
> Adds a helper function to send end touch events for active tracking ids.
> And also implements it in egalax driver.
What problematic scenario is this supposed to solve?
The 'release on suspend' is only an approximation to the 'close laptop' scenario,
it is certainly not correct in the coffee table case, for instance. Instead of
hardcoding this in the kernel, userland can easily detect long intervals
directly using the event time.
Unless there is already a reasonable mechanism in your user application which
deals with cases like this but fails to account for your particular problem
scenario, the patch will not be applied.
Thanks,
Henrik
^ permalink raw reply
* Re: [PATCH v4] input: Add LED support to Synaptics device
From: Pali Rohár @ 2013-12-31 10:16 UTC (permalink / raw)
To: Takashi Iwai, Dmitry Torokhov, Pavel Machek, Richard Purdie
Cc: linux-input, linux-kernel
In-Reply-To: <201304202051.13656@pali>
[-- Attachment #1: Type: Text/Plain, Size: 7342 bytes --]
On Saturday 20 April 2013 20:51:13 Pali Rohár wrote:
> On Thursday 22 April 2010 08:16:42 Takashi Iwai wrote:
> > diff --git a/drivers/input/mouse/Kconfig
> > b/drivers/input/mouse/Kconfig index c714ca2..91d3517 100644
> > --- a/drivers/input/mouse/Kconfig
> > +++ b/drivers/input/mouse/Kconfig
> > @@ -19,6 +19,7 @@ config MOUSE_PS2
> >
> > select SERIO_LIBPS2
> > select SERIO_I8042 if X86
> > select SERIO_GSCPS2 if GSC
> >
> > + select LEDS_CLASS if MOUSE_PS2_SYNAPICS_LED
> >
> > help
> >
> > Say Y here if you have a PS/2 mouse connected to your
> >
> > system. This includes the standard 2 or 3-button PS/2 mouse,
> > as well as PS/2 @@ -67,6 +68,14 @@ config
> > MOUSE_PS2_SYNAPTICS
> >
> > If unsure, say Y.
> >
> > +config MOUSE_PS2_SYNAPTICS_LED
> > + bool "Support embedded LED on Synaptics devices"
> > + depends on MOUSE_PS2_SYNAPTICS
> > + select NEW_LEDS
> > + help
> > + Say Y here if you have a Synaptics device with an
> > embedded LED. + This will enable LED class driver to
> > control the LED device. +
> >
> > config MOUSE_PS2_LIFEBOOK
> >
> > bool "Fujitsu Lifebook PS/2 mouse protocol extension" if
> >
> > EMBEDDED default y
> > diff --git a/drivers/input/mouse/synaptics.c
> > b/drivers/input/mouse/synaptics.c index c7b5285..8dc1fb5
> > 100644
> > --- a/drivers/input/mouse/synaptics.c
> > +++ b/drivers/input/mouse/synaptics.c
> > @@ -28,6 +28,7 @@
> >
> > #include <linux/input.h>
> > #include <linux/serio.h>
> > #include <linux/libps2.h>
> >
> > +#include <linux/leds.h>
> >
> > #include <linux/slab.h>
> > #include "psmouse.h"
> > #include "synaptics.h"
> >
> > @@ -335,6 +336,110 @@ static void synaptics_pt_create(struct
> > psmouse *psmouse) serio_register_port(serio);
> >
> > }
> >
> > +#ifdef CONFIG_MOUSE_PS2_SYNAPTICS_LED
> > +/*
> > + * LED handling:
> > + * Some Synaptics devices have an embeded LED at the
> > top-left corner. + */
> > +
> > +struct synaptics_led {
> > + struct psmouse *psmouse;
> > + struct work_struct work;
> > + struct led_classdev cdev;
> > +};
> > +
> > +static void synaptics_set_led(struct psmouse *psmouse, int
> > on) +{
> > + int i;
> > + unsigned char cmd = on ? 0x88 : 0x10;
> > +
> > + ps2_begin_command(&psmouse->ps2dev);
> > + if (__ps2_command(&psmouse->ps2dev, NULL,
> > PSMOUSE_CMD_SETSCALE11)) + goto out;
> > + for (i = 6; i >= 0; i -= 2) {
> > + unsigned char d = (cmd >> i) & 3;
> > + if (__ps2_command(&psmouse->ps2dev, &d,
> > PSMOUSE_CMD_SETRES)) + goto out;
> > + }
> > + cmd = 0x0a;
> > + __ps2_command(&psmouse->ps2dev, &cmd,
> > PSMOUSE_CMD_SETRATE); + out:
> > + ps2_end_command(&psmouse->ps2dev);
> > +}
> > +
> > +static void synaptics_led_work(struct work_struct *work)
> > +{
> > + struct synaptics_led *led;
> > +
> > + led = container_of(work, struct synaptics_led, work);
> > + synaptics_set_led(led->psmouse, led->cdev.brightness);
> > +}
> > +
> > +static void synaptics_led_cdev_brightness_set(struct
> > led_classdev *cdev, + enum led_brightness
>
> value)
>
> > +{
> > + struct synaptics_led *led;
> > +
> > + led = container_of(cdev, struct synaptics_led, cdev);
> > + schedule_work(&led->work);
> > +}
> > +
> > +static void synaptics_sync_led(struct psmouse *psmouse)
> > +{
> > + struct synaptics_data *priv = psmouse->private;
> > +
> > + if (priv->led)
> > + synaptics_set_led(psmouse, priv->led-
>cdev.brightness);
> > +}
> > +
> > +static int synaptics_init_led(struct psmouse *psmouse)
> > +{
> > + struct synaptics_data *priv = psmouse->private;
> > + struct synaptics_led *led;
> > + int err;
> > +
> > + /* FIXME: LED is supposedly detectable in cap0c[1] 0x20,
> > but it seems + * not working on real machines.
> > + * So we check the product id to be sure.
> > + */
> > + if (!priv->ext_cap_0c || SYN_CAP_PRODUCT_ID(priv-
>ext_cap)
> > != 0xe4) + return 0;
> > +
> > + printk(KERN_INFO "synaptics: support LED control\n");
> > + led = kzalloc(sizeof(struct synaptics_led), GFP_KERNEL);
> > + if (!led)
> > + return -ENOMEM;
> > + led->psmouse = psmouse;
> > + INIT_WORK(&led->work, synaptics_led_work);
> > + led->cdev.name = "psmouse::synaptics";
> > + led->cdev.brightness_set =
> > synaptics_led_cdev_brightness_set; + led->cdev.flags =
> > LED_CORE_SUSPENDRESUME;
> > + err = led_classdev_register(NULL, &led->cdev);
> > + if (err < 0) {
> > + kfree(led);
> > + return err;
> > + }
> > + priv->led = led;
> > + return 0;
> > +}
> > +
> > +static void synaptics_free_led(struct psmouse *psmouse)
> > +{
> > + struct synaptics_data *priv = psmouse->private;
> > +
> > + if (!priv->led)
> > + return;
> > + cancel_work_sync(&priv->led->work);
> > + synaptics_set_led(psmouse, 0);
> > + led_classdev_unregister(&priv->led->cdev);
> > + kfree(priv->led);
> > +}
> > +#else
> > +#define synaptics_init_led(ps) 0
> > +#define synaptics_free_led(ps) do {} while (0)
> > +#define synaptics_sync_led(ps) do {} while (0)
> > +#endif
> > +
> >
> > /**********************************************************
> > **
> >
> > ***************** * Functions to interpret the absolute mode
> > packets
> >
> > ************************************************************
> > * ***************/ @@ -622,6 +727,7 @@ static void
> > set_input_params(struct input_dev *dev, struct
> > synaptics_data *priv)
> >
> > static void synaptics_disconnect(struct psmouse *psmouse)
> > {
> >
> > + synaptics_free_led(psmouse);
> >
> > synaptics_reset(psmouse);
> > kfree(psmouse->private);
> > psmouse->private = NULL;
> >
> > @@ -653,6 +759,8 @@ static int synaptics_reconnect(struct
> > psmouse *psmouse) return -1;
> >
> > }
> >
> > + synaptics_sync_led(psmouse);
> > +
> >
> > return 0;
> >
> > }
> >
> > @@ -727,6 +835,9 @@ int synaptics_init(struct psmouse
> > *psmouse) SYN_ID_MAJOR(priv->identity),
> > SYN_ID_MINOR(priv->identity), priv->model_id,
> > priv->capabilities, priv->ext_cap, priv->ext_cap_0c);
> >
> > + if (synaptics_init_led(psmouse) < 0)
> > + goto init_fail;
> > +
> >
> > set_input_params(psmouse->dev, priv);
> >
> > /*
> >
> > diff --git a/drivers/input/mouse/synaptics.h
> > b/drivers/input/mouse/synaptics.h index ae37c5d..d5bb8f4
> > 100644
> > --- a/drivers/input/mouse/synaptics.h
> > +++ b/drivers/input/mouse/synaptics.h
> > @@ -94,6 +94,8 @@ struct synaptics_hw_state {
> >
> > signed char scroll;
> >
> > };
> >
> > +struct synaptics_led;
> > +
> >
> > struct synaptics_data {
> >
> > /* Data read from the touchpad */
> > unsigned long int model_id; /* Model-ID */
> >
> > @@ -107,6 +109,7 @@ struct synaptics_data {
> >
> > unsigned char pkt_type; /* packet type - old, new, etc
>
> */
>
> > unsigned char mode; /* current mode byte */
> > int scroll;
> >
> > + struct synaptics_led *led;
> >
> > };
> >
> > void synaptics_module_init(void);
>
> Hello,
>
> what happened with this patch? There is no discussion about
> v4: https://patchwork.kernel.org/patch/94026/
> https://lkml.org/lkml/2010/4/22/35
>
> Are there any problems with this patch?
BUMP!
--
Pali Rohár
pali.rohar@gmail.com
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply
* [PATCH v4] Add ff-memless-next module
From: Michal Malý @ 2013-12-31 10:41 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-input, linux-kernel, elias.vds, rob
Introduce ff-memless-next module as a possible future replacement for
ff-memless.
Cc: <linux-input@vger.kernel.org>
Cc: <linux-kernel@vger.kernel.org>
Cc: Rob Landley <rob@landley.net>
Tested-by: Elias Vanderstuyft <elias.vds@gmail.com>
Signed-off-by: Michal Malý <madcatxster@prifuk.cz>
---
v4: Fix a typo in the documentation file
v3: Use proper email format
v2: Remove unnecessary "else" branches
Documentation/input/ff-memless-next.txt | 149 ++++++
drivers/input/Kconfig | 12 +
drivers/input/Makefile | 2 +
drivers/input/ff-memless-next.c | 793 ++++++++++++++++++++++++++++++++
include/linux/input/ff-memless-next.h | 31 ++
5 files changed, 987 insertions(+)
create mode 100644 Documentation/input/ff-memless-next.txt
create mode 100644 drivers/input/ff-memless-next.c
create mode 100644 include/linux/input/ff-memless-next.h
diff --git a/Documentation/input/ff-memless-next.txt b/Documentation/input/ff-memless-next.txt
new file mode 100644
index 0000000..699dc04
--- /dev/null
+++ b/Documentation/input/ff-memless-next.txt
@@ -0,0 +1,149 @@
+"ff-memless-next" force feedback module for memoryless devices.
+By Michal Malý <madcatxster@prifuk.cz> on 2013/12/21
+----------------------------------------------------------------------------
+
+1. Introduction
+~~~~~~~~~~~~~~~
+This document describes basic working principles of the "ff-memless-next"
+module and its purpose. This document also contains a summary
+of the "ff-memless-next" API and brief instructions on how to use it to write
+a hardware-specific backend with "ff-memless-next". Some specifics
+of ff-memless-next that might be of interest for userspace developers
+are also discussed.
+
+2. Basic principles of ff-memless-next
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+A lot of commonly used force feedback devices do not have a memory of their
+own. This means that it is not possible to upload a force feedback effect
+to such a device and let the device's CPU handle the playback. Instead,
+the device relies solely on its driver to tell it what force to generate.
+"ff-memless-next" was written to serve in this capacity. Its purpose is to
+calculate the overall force the device should apply and pass the result to
+a hardware-specific backend which then submits the appropriate command to
+the device.
+
+"ff-memless-next" differentiates between two types of force feedback effects,
+"combinable" and "uncombinable".
+"Combinable" effects are effects that generate a force of a given
+magnitude and direction. The magnitude can change in time according to the
+envelope of the effect and its waveform; the latter applies only to periodic
+effects. Force generated by "combinable" effect does not depend on the position
+of the device. Forces generated by each "combinable" effect that is active
+are periodically recalculated as needed and superposed into one overall force.
+"Combinable" effects are FF_CONSTANT, FF_PERIODIC and FF_RAMP.
+
+"Uncombinable" effects generate a force that depends on the position of
+the device. "ff-memless-next" assumes that the device takes care of processing
+such an effect. However, a device might have a limit on how many "uncombinable"
+effects can be active at once and this limit might be lower than the maximum
+effect count set in "ff-memless-next". "ff-memless-next" allows a
+hardware-specific driver to check whether the device is able to play
+an "uncombinable" effect. Error is reported back to userspace if the device
+cannot play the effect. The hardware-specific driver may choose not to
+perform this check in which case the userspace will have no way of knowing
+whether an "uncombinable" effect is really being played or not.
+"Uncombinable" effects are FF_DAMPER, FF_FRICTION, FF_INERTIA and FF_SPRING.
+
+FF_CUSTOM is currently not handled by "ff-memless-next".
+
+3. API provided by "ff-memless-next"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"ff-memless-next" provides an API for developers of hardware-specific
+drivers. In order to use the API, the hardware-specific driver should
+include <linux/input/ff-memless-next.h>
+Functions and structs defined by this API are:
+
+int input_ff_create_mlnx(struct input_dev *dev, void *data,
+ int(*control_effect)(struct input_dev *, void *, const struct mlnx_effect_command *),
+ const u16 update_rate)
+- Any hardware-specific driver that wants to use "ff-memless-next" must call
+this function. The function takes care of creating and registering a force
+feedback device within the kernel. It also initializes resources needed by
+"ff-memless-next" to handle a new device. No other initialization steps are necessary.
+ Parameters:
+ * dev - pointer to valid struct input_dev
+ * data - pointer to custom data the hardware-specific backend
+ might need to pass to the control_effect() callback function
+ (discussed later). * data will be freed automatically by
+ "ff-memless-next" upon device's destruction.
+ * control_effect - A callback function. "ff-memless-next" will call
+ this function when it is done processing effects.
+ Implementation of control_effect() in the
+ hardware-specific driver should create an appropriate
+ command and submit it to the device.
+ This function is called with dev->event_lock
+ spinlock held.
+ update_rate - Rate in milliseconds at which envelopes and periodic
+ effects are recalculated. Minimum value is limited by the
+ kernel timer resolution and changes with value of
+ CONFIG_HZ.
+
+struct mlnx_effect_command
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+- This struct is passed to the hardware-specific backend in
+the control_effect() function. See "ff-memless-next.h" for details.
+
+enum mlnx_commands
+^^^^^^^^^^^^^^^^^^
+- Types of commands generated by ff-memless-next
+ MLNX_START_COMBINED - Start or update "combinable" effect
+ MLNX_STOP_COMBINED - Stop "combinable" effect. In most cases this means
+ to set the force to zero.
+ MLNX_UPLOAD_UNCOMB - Check if the device can accept and play
+ "uncombinable" effect.
+ Hardware-specific driver should return 0
+ on success.
+ MLNX_START_UNCOMB - Start playback of "uncombinable" effect.
+ MLNX_STOP_UNCOMB - Stop playback of "uncombinable" effect.
+
+struct mlnx_simple_force
+^^^^^^^^^^^^^^^^^^^^^^^^
+ x - overall force along X axis
+ y - overall force along Y axis
+
+struct mlnx_uncomb_effect
+^^^^^^^^^^^^^^^^^^^^^^^^^
+- Information about "uncombinable" effect.
+ id - internal ID of the effect
+ * effect - pointer to the internal copy of the effect kept by
+ "ff-memless-next". This pointer will remain valid until
+ the effect is removed.
+
+Sample implementation of a dummy driver that uses "ff-memless-next" is
+available at "git://prifuk.cz/ff-dummy-device". Link to the source will
+be kept up to date.
+
+Direction of the effect's force translates to Cartesian coordinates system
+as follows:
+ Direction = 0: Down
+ Direction (0; 16384): 3rd quadrant
+ Direction = 16384: Left
+ Direction (16385; 32768): 2nd quadrant
+ Direction = 32768: Up
+ Direction (32769; 49152): 1st quadrant
+ Direction = 49152: Right
+ Direction (49153; 65535) :4th quadrant
+ Direction = 65565: Down
+
+4. Specifics of "ff-memless-next" for userspace developers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+None of the persons involved in development or testing of "ff-memless-next"
+is aware of any reference force feedback specifications. "ff-memless-next"
+tries to adhere to Microsoft's DirectInput specifications because we
+believe that is what most developers have experience with.
+
+- Waveforms of periodic effects do not start at the origin, but as follows:
+ SAW_UP: At minimum
+ SAW_DOWN: At maximum
+ SQUARE: At maximum
+ TRIANGLE: At maximum
+ SINE: At origin
+
+- Updating periodic effects:
+ - All periodic effects are restarted when their parameters are updated.
+
+- Updating effects of finite duration:
+ - Once an effect of finite length finishes playing, it is considered
+ stopped. Only effects that are playing can be updated on the fly.
+ Therefore effects of finite duration can be updated only until
+ they finish playing.
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index a11ff74..4371a12 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -77,6 +77,18 @@ config INPUT_MATRIXKMAP
To compile this driver as a module, choose M here: the
module will be called matrix-keymap.
+config INPUT_FF_MEMLESS_NEXT
+ tristate "New version of support for memoryless force feedback devices"
+ help
+ Say Y here if you want to enable support for various memoryless
+ force feedback devices (as of now there is no hardware-specific
+ driver that supports this).
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ff-memless-next.
+
comment "Userland interfaces"
config INPUT_MOUSEDEV
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 5ca3f63..269a22d 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -16,6 +16,8 @@ obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
obj-$(CONFIG_INPUT_EVDEV) += evdev.o
obj-$(CONFIG_INPUT_EVBUG) += evbug.o
+obj-$(CONFIG_INPUT_MATRIXKMAP) += matrix-keymap.o
+obj-$(CONFIG_INPUT_FF_MEMLESS_NEXT) += ff-memless-next.o
obj-$(CONFIG_INPUT_KEYBOARD) += keyboard/
obj-$(CONFIG_INPUT_MOUSE) += mouse/
diff --git a/drivers/input/ff-memless-next.c b/drivers/input/ff-memless-next.c
new file mode 100644
index 0000000..97de791
--- /dev/null
+++ b/drivers/input/ff-memless-next.c
@@ -0,0 +1,793 @@
+/*
+ * Force feedback support for memoryless devices
+ *
+ * This module is based on "ff-memless" orignally written by Anssi Hannula.
+ * It is extended to support all force feedback effects currently supported
+ * by the Linux input stack.
+ *
+ * Copyright(c) 2013 Michal Maly <madcatxster@prifuk.cz>
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/jiffies.h>
+#include <linux/fixp-arith.h>
+#include <linux/input/ff-memless-next.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michal \"MadCatX\" Maly");
+MODULE_DESCRIPTION("Force feedback support for memoryless force feedback devices");
+
+#define FF_MAX_EFFECTS 16
+#define FF_MIN_EFFECT_LENGTH ((1000 / CONFIG_HZ) + 1)
+#define FF_EFFECT_STARTED 1
+#define FF_EFFECT_PLAYING 2
+
+
+struct mlnx_effect {
+ struct ff_effect effect;
+ unsigned long flags;
+ unsigned long begin_at;
+ unsigned long stop_at;
+ unsigned long updated_at;
+ unsigned long attack_stop;
+ unsigned long fade_begin;
+ int repeat;
+ u16 playback_time;
+};
+
+struct mlnx_device {
+ u8 combinable_playing;
+ unsigned long update_rate_jiffies;
+ void *private;
+ struct mlnx_effect effects[FF_MAX_EFFECTS];
+ int gain;
+ struct timer_list timer;
+ struct input_dev *dev;
+
+ int (*control_effect)(struct input_dev *, void *,
+ const struct mlnx_effect_command *);
+};
+
+static s32 mlnx_calculate_x_force(const s32 level,
+ const u16 direction)
+{
+ s32 new = (level * -fixp_sin(direction)) >> FRAC_N;
+ pr_debug("x force: %d\n", new);
+ return new;
+}
+
+static s32 mlnx_calculate_y_force(const s32 level,
+ const u16 direction)
+{
+ s32 new = (level * -fixp_cos(direction)) >> FRAC_N;
+ pr_debug("y force: %d\n", new);
+ return new;
+}
+
+static bool mlnx_is_combinable(const struct ff_effect *effect)
+{
+ switch (effect->type) {
+ case FF_CONSTANT:
+ case FF_PERIODIC:
+ case FF_RAMP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mlnx_is_conditional(const struct ff_effect *effect)
+{
+ switch (effect->type) {
+ case FF_DAMPER:
+ case FF_FRICTION:
+ case FF_INERTIA:
+ case FF_SPRING:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mlnx_is_square(const struct ff_effect *effect)
+{
+ if (effect->type == FF_PERIODIC)
+ return effect->u.periodic.waveform == FF_SQUARE;
+ return false;
+}
+
+static void mlnx_clr_playing(struct mlnx_effect *mlnxeff)
+{
+ __clear_bit(FF_EFFECT_PLAYING, &mlnxeff->flags);
+}
+
+static void mlnx_clr_started(struct mlnx_effect *mlnxeff)
+{
+ __clear_bit(FF_EFFECT_STARTED, &mlnxeff->flags);
+}
+
+static bool mlnx_is_playing(const struct mlnx_effect *mlnxeff)
+{
+ return test_bit(FF_EFFECT_PLAYING, &mlnxeff->flags);
+}
+
+static bool mlnx_is_started(const struct mlnx_effect *mlnxeff)
+{
+ return test_bit(FF_EFFECT_STARTED, &mlnxeff->flags);
+}
+
+static bool mlnx_test_set_playing(struct mlnx_effect *mlnxeff)
+{
+ return test_and_set_bit(FF_EFFECT_PLAYING, &mlnxeff->flags);
+}
+
+static const struct ff_envelope *mlnx_get_envelope(const struct ff_effect *effect)
+{
+ static const struct ff_envelope empty;
+
+ switch (effect->type) {
+ case FF_CONSTANT:
+ return &effect->u.constant.envelope;
+ case FF_PERIODIC:
+ return &effect->u.periodic.envelope;
+ case FF_RAMP:
+ return &effect->u.ramp.envelope;
+ default:
+ return ∅
+ }
+}
+
+/* Some devices might have a limit on how many uncombinable effects
+ * can be played at once */
+static int mlnx_upload_conditional(struct mlnx_device *mlnxdev,
+ const struct ff_effect *effect)
+{
+ struct mlnx_effect_command ecmd = {
+ .cmd = MLNX_UPLOAD_UNCOMB,
+ .u.uncomb.id = effect->id,
+ .u.uncomb.effect = effect
+ };
+ return mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &ecmd);
+}
+
+static void mlnx_set_envelope_times(struct mlnx_effect *mlnxeff)
+{
+ const struct ff_effect *effect = &mlnxeff->effect;
+ const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+
+ if (envelope->attack_length) {
+ unsigned long j = msecs_to_jiffies(envelope->attack_length);
+ mlnxeff->attack_stop = mlnxeff->begin_at + j;
+ }
+ if (effect->replay.length && envelope->fade_length) {
+ unsigned long j = msecs_to_jiffies(envelope->fade_length);
+ mlnxeff->fade_begin = mlnxeff->stop_at - j;
+ }
+}
+
+static void mlnx_set_trip_times(struct mlnx_effect *mlnxeff,
+ const unsigned long now)
+{
+ const struct ff_effect *effect = &mlnxeff->effect;
+ const u16 replay_delay = effect->replay.delay;
+ const u16 replay_length = effect->replay.length;
+
+ mlnxeff->begin_at = now + msecs_to_jiffies(replay_delay);
+ mlnxeff->stop_at = mlnxeff->begin_at + msecs_to_jiffies(replay_length);
+ mlnxeff->updated_at = mlnxeff->begin_at;
+ if (effect->type == FF_PERIODIC)
+ mlnxeff->playback_time = effect->u.periodic.phase;
+}
+
+static void mlnx_start_effect(struct mlnx_effect *mlnxeff)
+{
+ const unsigned long now = jiffies;
+
+ mlnx_set_trip_times(mlnxeff, now);
+ mlnx_set_envelope_times(mlnxeff);
+ __set_bit(FF_EFFECT_STARTED, &mlnxeff->flags);
+}
+
+static void mlnx_stop_effect(struct mlnx_device *mlnxdev,
+ const struct mlnx_effect *mlnxeff)
+{
+ switch (mlnxeff->effect.type) {
+ case FF_CONSTANT:
+ case FF_PERIODIC:
+ case FF_RAMP:
+ if (--mlnxdev->combinable_playing == 0) {
+ const struct mlnx_effect_command c = {
+ .cmd = MLNX_STOP_COMBINED
+ };
+ mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private,
+ &c);
+ }
+ return;
+ case FF_DAMPER:
+ case FF_FRICTION:
+ case FF_INERTIA:
+ case FF_SPRING:
+ {
+ const struct mlnx_effect_command c = {
+ .cmd = MLNX_STOP_UNCOMB,
+ .u.uncomb.id = mlnxeff->effect.id,
+ .u.uncomb.effect = &mlnxeff->effect
+ };
+ mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &c);
+ return;
+ }
+ default:
+ return;
+ }
+}
+
+static int mlnx_restart_effect(struct mlnx_device *mlnxdev,
+ struct mlnx_effect *mlnxeff)
+{
+ const struct ff_effect *effect = &mlnxeff->effect;
+ const unsigned long now = jiffies;
+
+ if (mlnx_is_combinable(effect)) {
+ if (effect->replay.delay)
+ mlnx_stop_effect(mlnxdev, mlnxeff);
+ else
+ mlnxdev->combinable_playing--;
+ } else if (mlnx_is_conditional(effect)) {
+ int ret;
+ if (effect->replay.delay)
+ mlnx_stop_effect(mlnxdev, mlnxeff);
+
+ ret = mlnx_upload_conditional(mlnxdev, &mlnxeff->effect);
+ if (ret)
+ return ret;
+ }
+
+ mlnx_set_trip_times(mlnxeff, now);
+ mlnx_set_envelope_times(mlnxeff);
+
+ return 0;
+}
+
+static s32 mlnx_apply_envelope(const struct mlnx_effect *mlnxeff,
+ const s32 level)
+{
+ const struct ff_effect *effect = &mlnxeff->effect;
+ const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+ const unsigned long now = jiffies;
+ const s32 alevel = abs(level);
+
+ /* Effect has an envelope with nonzero attack time */
+ if (envelope->attack_length && time_before(now, mlnxeff->attack_stop)) {
+ const s32 clength = jiffies_to_msecs(now - mlnxeff->begin_at);
+ const s32 alength = envelope->attack_length;
+ const s32 dlevel = (alevel - envelope->attack_level)
+ * clength / alength;
+ return level < 0 ? -(dlevel + envelope->attack_level) :
+ (dlevel + envelope->attack_level);
+ } else if (envelope->fade_length && time_before_eq(mlnxeff->fade_begin, now)) {
+ const s32 clength = jiffies_to_msecs(now - mlnxeff->fade_begin);
+ const s32 flength = envelope->fade_length;
+ const s32 dlevel = (envelope->fade_level - alevel)
+ * clength / flength;
+ return level < 0 ? -(dlevel + alevel) : (dlevel + alevel);
+ }
+
+ return level;
+}
+
+static s32 mlnx_calculate_periodic(struct mlnx_effect *mlnxeff, const s32 level)
+{
+ const struct ff_effect *effect = &mlnxeff->effect;
+ const unsigned long now = jiffies;
+ const u16 period = effect->u.periodic.period;
+ const unsigned long dt = jiffies_to_msecs(now - mlnxeff->updated_at);
+ const s16 offset = effect->u.periodic.offset;
+ s32 new = level;
+ u16 t;
+
+ mlnxeff->playback_time += dt;
+ t = mlnxeff->playback_time % period;
+
+ switch (effect->u.periodic.waveform) {
+ case FF_SINE:
+ {
+ u16 degrees = (360 * t) / period;
+ new = ((level * fixp_sin(degrees)) >> FRAC_N) + offset;
+ break;
+ }
+ case FF_SQUARE:
+ {
+ u16 degrees = (360 * t) / period;
+ new = level * (degrees < 180 ? 1 : -1) + offset;
+ break;
+ }
+ case FF_SAW_UP:
+ new = 2 * level * t / period - level + offset;
+ break;
+ case FF_SAW_DOWN:
+ new = level - 2 * level * t / period + offset;
+ break;
+ case FF_TRIANGLE:
+ {
+ new = (2 * abs(level - (2 * level * t) / period));
+ new = new - level + offset;
+ break;
+ }
+ case FF_CUSTOM:
+ pr_debug("Custom waveform is not handled by this driver\n");
+ return level;
+ default:
+ pr_err("Invalid waveform\n");
+ return level;
+ }
+
+ mlnxeff->playback_time = t;
+ /* Ensure that the offset did not make the value exceed s16 range */
+ new = clamp(new, -0x7fff, 0x7fff);
+ pr_debug("level: %d, playback: %u, t: %u, dt: %lu\n",
+ new, mlnxeff->playback_time, t, dt);
+ return new;
+}
+
+static s32 mlnx_calculate_ramp(const struct mlnx_effect *mlnxeff)
+{
+ const struct ff_effect *effect = &mlnxeff->effect;
+ const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+ const unsigned long now = jiffies;
+ const u16 length = effect->replay.length;
+ const s16 mean = (effect->u.ramp.start_level + effect->u.ramp.end_level) / 2;
+ const u16 t = jiffies_to_msecs(now - mlnxeff->begin_at);
+ s32 start = effect->u.ramp.start_level;
+ s32 end = effect->u.ramp.end_level;
+ s32 new;
+
+ if (envelope->attack_length && time_before(now, mlnxeff->attack_stop)) {
+ const s32 clength = jiffies_to_msecs(now - mlnxeff->begin_at);
+ const s32 alength = envelope->attack_length;
+ s32 attack_level;
+ if (end > start)
+ attack_level = mean - envelope->attack_level;
+ else
+ attack_level = mean + envelope->attack_level;
+ start = (start - attack_level) * clength / alength
+ + attack_level;
+ } else if (envelope->fade_length && time_before_eq(mlnxeff->fade_begin, now)) {
+ const s32 clength = jiffies_to_msecs(now - mlnxeff->fade_begin);
+ const s32 flength = envelope->fade_length;
+ s32 fade_level;
+ if (end > start)
+ fade_level = mean + envelope->fade_level;
+ else
+ fade_level = mean - envelope->fade_level;
+ end = (fade_level - end) * clength / flength + end;
+ }
+
+ new = ((end - start) * t) / length + start;
+ new = clamp(new, -0x7fff, 0x7fff);
+ pr_debug("RAMP level: %d, t: %u\n", new, t);
+ return new;
+}
+
+static void mlnx_destroy(struct ff_device *dev)
+{
+ struct mlnx_device *mlnxdev = dev->private;
+ del_timer_sync(&mlnxdev->timer);
+
+ kfree(mlnxdev->private);
+}
+
+static unsigned long mlnx_get_envelope_update_time(const struct mlnx_effect *mlnxeff,
+ const unsigned long update_rate_jiffies)
+{
+ const struct ff_effect *effect = &mlnxeff->effect;
+ const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+ const unsigned long now = jiffies;
+ unsigned long fade_next;
+
+ /* Effect has an envelope with nonzero attack time */
+ if (envelope->attack_length && time_before(now, mlnxeff->attack_stop)) {
+ if (time_before(mlnxeff->updated_at + update_rate_jiffies, mlnxeff->attack_stop))
+ return mlnxeff->updated_at + update_rate_jiffies;
+
+ return mlnxeff->attack_stop;
+ }
+
+ /* Effect has an envelope with nonzero fade time */
+ if (mlnxeff->effect.replay.length) {
+ if (!envelope->fade_length)
+ return mlnxeff->stop_at;
+
+ /* Schedule the next update when the fade begins */
+ if (time_before(mlnxeff->updated_at, mlnxeff->fade_begin))
+ return mlnxeff->fade_begin;
+
+ /* Already fading */
+ fade_next = mlnxeff->updated_at + update_rate_jiffies;
+
+ /* Schedule update when the effect stops */
+ if (time_after(fade_next, mlnxeff->stop_at))
+ return mlnxeff->stop_at;
+ /* Schedule update at the next checkpoint */
+ return fade_next;
+ }
+
+ /* There is no envelope */
+
+ /* Prevent the effect from being started twice */
+ if (mlnxeff->begin_at == now && mlnx_is_playing(mlnxeff))
+ return now - 1;
+
+ return mlnxeff->begin_at;
+}
+
+static unsigned long mlnx_get_update_time(const struct mlnx_effect *mlnxeff,
+ const unsigned long update_rate_jiffies)
+{
+ const unsigned long now = jiffies;
+ unsigned long time, update_periodic;
+
+ switch (mlnxeff->effect.type) {
+ /* Constant effect does not change with time, but it can have
+ * an envelope and a duration */
+ case FF_CONSTANT:
+ return mlnx_get_envelope_update_time(mlnxeff,
+ update_rate_jiffies);
+ /* Periodic and ramp effects have to be periodically updated */
+ case FF_PERIODIC:
+ case FF_RAMP:
+ time = mlnx_get_envelope_update_time(mlnxeff,
+ update_rate_jiffies);
+
+ if (mlnx_is_square(&mlnxeff->effect)) {
+ const u16 half_T = mlnxeff->effect.u.periodic.period/2;
+ update_periodic = msecs_to_jiffies(half_T)
+ + mlnxeff->updated_at;
+ } else
+ update_periodic = mlnxeff->updated_at
+ + update_rate_jiffies;
+
+ /* Periodic effect has to be updated earlier than envelope
+ * or envelope update time is in the past */
+ if (time_before(update_periodic, time) || time_before(time, now))
+ return update_periodic;
+ /* Envelope needs to be updated */
+ return time;
+ case FF_DAMPER:
+ case FF_FRICTION:
+ case FF_INERTIA:
+ case FF_SPRING:
+ default:
+ if (time_after_eq(mlnxeff->begin_at, now))
+ return mlnxeff->begin_at;
+
+ return mlnxeff->stop_at;
+ }
+}
+
+static void mlnx_schedule_playback(struct mlnx_device *mlnxdev)
+{
+ struct mlnx_effect *mlnxeff;
+ const unsigned long now = jiffies;
+ int events = 0;
+ int i;
+ unsigned long earliest = 0;
+ unsigned long time;
+
+ /* Iterate over all effects and determine the earliest
+ * time when we have to attend to any */
+ for (i = 0; i < FF_MAX_EFFECTS; i++) {
+ mlnxeff = &mlnxdev->effects[i];
+
+ if (!mlnx_is_started(mlnxeff))
+ continue; /* Effect is not started, skip it */
+
+ if (mlnx_is_playing(mlnxeff))
+ time = mlnx_get_update_time(mlnxeff,
+ mlnxdev->update_rate_jiffies);
+ else
+ time = mlnxeff->begin_at;
+
+ pr_debug("Update time for effect %d: %lu\n", i, time);
+
+ /* Scheduled time is in the future and is either
+ * before the current earliest time or it is
+ * the first valid time value in this pass */
+ if (time_before_eq(now, time) &&
+ (++events == 1 || time_before(time, earliest)))
+ earliest = time;
+ }
+
+ if (events) {
+ pr_debug("Events: %d, earliest: %lu\n", events, earliest);
+ mod_timer(&mlnxdev->timer, earliest);
+ }
+}
+
+static void mlnx_add_force(struct mlnx_effect *mlnxeff, s32 *cfx, s32 *cfy,
+ const u16 gain)
+{
+ const struct ff_effect *effect = &mlnxeff->effect;
+ u16 direction;
+ s32 level;
+
+ pr_debug("Processing effect type %d, ID %d\n",
+ mlnxeff->effect.type, mlnxeff->effect.id);
+
+ direction = mlnxeff->effect.direction * 360 / 0xffff;
+ pr_debug("Direction deg: %u\n", direction);
+
+ switch (mlnxeff->effect.type) {
+ case FF_CONSTANT:
+ level = mlnx_apply_envelope(mlnxeff, effect->u.constant.level);
+ break;
+ case FF_PERIODIC:
+ level = mlnx_apply_envelope(mlnxeff,
+ effect->u.periodic.magnitude);
+ level = mlnx_calculate_periodic(mlnxeff, level);
+ break;
+ case FF_RAMP:
+ level = mlnx_calculate_ramp(mlnxeff);
+ break;
+ default:
+ pr_err("Effect %d not handled by mlnx_add_force\n",
+ mlnxeff->effect.type);
+ return;
+ }
+
+ *cfx += mlnx_calculate_x_force(level, direction) * gain / 0xffff;
+ *cfy += mlnx_calculate_y_force(level, direction) * gain / 0xffff;
+}
+
+static void mlnx_play_effects(struct mlnx_device *mlnxdev)
+{
+ const u16 gain = mlnxdev->gain;
+ const unsigned long now = jiffies;
+ int i;
+ int cfx = 0;
+ int cfy = 0;
+
+ for (i = 0; i < FF_MAX_EFFECTS; i++) {
+ struct mlnx_effect *mlnxeff = &mlnxdev->effects[i];
+
+ if (!mlnx_is_started(mlnxeff)) {
+ pr_debug("Effect %hd/%d not started\n",
+ mlnxeff->effect.id, i);
+ continue;
+ }
+
+ if (time_before(now, mlnxeff->begin_at)) {
+ pr_debug("Effect %hd/%d begins at a later time\n",
+ mlnxeff->effect.id, i);
+ continue;
+ }
+
+ if (time_before_eq(mlnxeff->stop_at, now) && mlnxeff->effect.replay.length) {
+ pr_debug("Effect %hd/%d has to be stopped\n",
+ mlnxeff->effect.id, i);
+
+ mlnx_clr_playing(mlnxeff);
+ if (--mlnxeff->repeat > 0)
+ mlnx_restart_effect(mlnxdev, mlnxeff);
+ else {
+ mlnx_clr_started(mlnxeff);
+ mlnx_stop_effect(mlnxdev, mlnxeff);
+ }
+
+ continue;
+ }
+
+ switch (mlnxeff->effect.type) {
+ case FF_CONSTANT:
+ case FF_PERIODIC:
+ case FF_RAMP:
+ if (!mlnx_test_set_playing(mlnxeff)) {
+ mlnxdev->combinable_playing++;
+ pr_debug("Starting combinable effect, total %u\n",
+ mlnxdev->combinable_playing);
+ }
+ mlnx_add_force(mlnxeff, &cfx, &cfy, gain);
+ break;
+ case FF_DAMPER:
+ case FF_FRICTION:
+ case FF_INERTIA:
+ case FF_SPRING:
+ if (!mlnx_test_set_playing(mlnxeff)) {
+ const struct mlnx_effect_command ecmd = {
+ .cmd = MLNX_START_UNCOMB,
+ .u.uncomb.id = i,
+ .u.uncomb.effect = &mlnxeff->effect
+ };
+ mlnxdev->control_effect(mlnxdev->dev,
+ mlnxdev->private, &ecmd);
+ }
+ break;
+ default:
+ pr_debug("Unhandled type of effect\n");
+ }
+ mlnxeff->updated_at = now;
+ }
+
+ if (mlnxdev->combinable_playing) {
+ const struct mlnx_effect_command ecmd = {
+ .cmd = MLNX_START_COMBINED,
+ .u.simple_force = {
+ .x = clamp(cfx, -0x7fff, 0x7fff),
+ .y = clamp(cfy, -0x7fff, 0x7fff)
+ }
+ };
+ mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &ecmd);
+ }
+
+ mlnx_schedule_playback(mlnxdev);
+}
+
+static void mlnx_set_gain(struct input_dev *dev, u16 gain)
+{
+ struct mlnx_device *mlnxdev = dev->ff->private;
+ int i;
+
+ mlnxdev->gain = gain;
+
+ for (i = 0; i < FF_MAX_EFFECTS; i++)
+ mlnx_clr_playing(&mlnxdev->effects[i]);
+
+ mlnx_play_effects(mlnxdev);
+}
+
+static int mlnx_startstop(struct input_dev *dev, int effect_id, int repeat)
+{
+ struct mlnx_device *mlnxdev = dev->ff->private;
+ struct mlnx_effect *mlnxeff = &mlnxdev->effects[effect_id];
+
+ if (repeat > 0) {
+ pr_debug("Starting effect ID %d\n", effect_id);
+ mlnxeff->repeat = repeat;
+ if (!mlnx_is_started(mlnxeff))
+ mlnx_start_effect(mlnxeff);
+ } else {
+ pr_debug("Stopping effect ID %d\n", effect_id);
+ if (mlnx_is_started(mlnxeff)) {
+ if (mlnx_is_playing(mlnxeff)) {
+ mlnx_clr_playing(mlnxeff);
+ mlnx_stop_effect(mlnxdev, mlnxeff);
+ }
+ mlnx_clr_started(mlnxeff);
+ } else {
+ pr_debug("Effect ID %d already stopped\n", effect_id);
+ return 0;
+ }
+ }
+ mlnx_play_effects(mlnxdev);
+
+ return 0;
+}
+
+static void mlnx_timer_fired(unsigned long data)
+{
+ struct input_dev *dev = (struct input_dev *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+ mlnx_play_effects(dev->ff->private);
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static int mlnx_upload(struct input_dev *dev, struct ff_effect *effect,
+ struct ff_effect *old)
+{
+ struct mlnx_device *mlnxdev = dev->ff->private;
+ struct mlnx_effect *mlnxeff = &mlnxdev->effects[effect->id];
+ const u16 length = effect->replay.length;
+ const u16 delay = effect->replay.delay;
+ int ret, fade_from;
+
+ /* Effect's timing is below kernel timer resolution */
+ if (length && length < FF_MIN_EFFECT_LENGTH)
+ effect->replay.length = FF_MIN_EFFECT_LENGTH;
+ if (delay && delay < FF_MIN_EFFECT_LENGTH)
+ effect->replay.delay = FF_MIN_EFFECT_LENGTH;
+
+ /* Periodic effects must have a non-zero period */
+ if (effect->type == FF_PERIODIC) {
+ if (!effect->u.periodic.period)
+ return -EINVAL;
+ }
+ /* Ramp effects cannot be infinite */
+ if (effect->type == FF_RAMP && !length)
+ return -EINVAL;
+
+ if (mlnx_is_combinable(effect)) {
+ const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+
+ /* Infinite effects cannot fade */
+ if (!length && envelope->fade_length > 0)
+ return -EINVAL;
+ /* Fade length cannot be greater than effect duration */
+ fade_from = (int)length - (int)envelope->fade_length;
+ if (fade_from < 0)
+ return -EINVAL;
+ /* Envelope cannot start fading before it finishes attacking */
+ if (fade_from < envelope->attack_length && fade_from > 0)
+ return -EINVAL;
+ }
+
+
+ spin_lock_irq(&dev->event_lock);
+ mlnxeff->effect = *effect; /* Keep internal copy of the effect */
+ /* Check if the effect being modified is playing */
+ if (mlnx_is_started(mlnxeff)) {
+ if (mlnx_is_playing(mlnxeff)) {
+ mlnx_clr_playing(mlnxeff);
+ ret = mlnx_restart_effect(mlnxdev, mlnxeff);
+
+ if (ret) {
+ /* Restore the original effect */
+ if (old)
+ mlnxeff->effect = *old;
+ spin_unlock_irq(&dev->event_lock);
+ return ret;
+ }
+ }
+
+ mlnx_schedule_playback(mlnxdev);
+ spin_unlock_irq(&dev->event_lock);
+ return 0;
+ }
+
+ if (mlnx_is_conditional(effect)) {
+ ret = mlnx_upload_conditional(mlnxdev, &mlnxeff->effect);
+ if (ret) {
+ /* Restore the original effect */
+ if (old)
+ mlnxeff->effect = *old;
+ spin_unlock_irq(&dev->event_lock);
+ return ret;
+ }
+ }
+
+ spin_unlock_irq(&dev->event_lock);
+
+ return 0;
+}
+
+int input_ff_create_mlnx(struct input_dev *dev, void *data,
+ int(*control_effect)(struct input_dev *, void *, const struct mlnx_effect_command *),
+ const u16 update_rate)
+{
+ struct mlnx_device *mlnxdev;
+ int ret;
+
+ mlnxdev = kzalloc(sizeof(*mlnxdev), GFP_KERNEL);
+ if (!mlnxdev)
+ return -ENOMEM;
+
+ mlnxdev->dev = dev;
+ mlnxdev->private = data;
+ mlnxdev->control_effect = control_effect;
+ mlnxdev->gain = 0xffff;
+ mlnxdev->update_rate_jiffies = update_rate < FF_MIN_EFFECT_LENGTH ?
+ FF_MIN_EFFECT_LENGTH : update_rate;
+ setup_timer(&mlnxdev->timer, mlnx_timer_fired, (unsigned long)dev);
+
+ ret = input_ff_create(dev, FF_MAX_EFFECTS);
+ if (ret) {
+ kfree(mlnxdev);
+ return ret;
+ }
+
+ dev->ff->private = mlnxdev;
+ dev->ff->upload = mlnx_upload;
+ dev->ff->set_gain = mlnx_set_gain;
+ dev->ff->destroy = mlnx_destroy;
+ dev->ff->playback = mlnx_startstop;
+
+ pr_debug("Device successfully registered.\n");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(input_ff_create_mlnx);
diff --git a/include/linux/input/ff-memless-next.h b/include/linux/input/ff-memless-next.h
new file mode 100644
index 0000000..f2fb080
--- /dev/null
+++ b/include/linux/input/ff-memless-next.h
@@ -0,0 +1,31 @@
+#include <linux/input.h>
+
+enum mlnx_commands {
+ MLNX_START_COMBINED,
+ MLNX_STOP_COMBINED,
+ MLNX_START_UNCOMB,
+ MLNX_STOP_UNCOMB,
+ MLNX_UPLOAD_UNCOMB
+};
+
+struct mlnx_simple_force {
+ const s32 x;
+ const s32 y;
+};
+
+struct mlnx_uncomb_effect {
+ const int id;
+ const struct ff_effect *effect;
+};
+
+struct mlnx_effect_command {
+ const enum mlnx_commands cmd;
+ union {
+ const struct mlnx_simple_force simple_force;
+ const struct mlnx_uncomb_effect uncomb;
+ } u;
+};
+
+int input_ff_create_mlnx(struct input_dev *dev, void *data,
+ int(*control_effect)(struct input_dev *, void *, const struct mlnx_effect_command *),
+ const u16 update_rate);
--
1.8.5.2
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [lm-sensors] [PATCH v2 2/5] input: sun4i-ts: Add support for temperature sensor
From: Hans de Goede @ 2013-12-31 10:57 UTC (permalink / raw)
To: Guenter Roeck
Cc: Dmitry Torokhov, LM Sensors, linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
Corentin LABBE, linux-input-u79uwXL29TY76Z2rM5mHXA, Maxime Ripard,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20131227163419.GA20135-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
Hi,
On 12/27/2013 05:34 PM, Guenter Roeck wrote:
> On Fri, Dec 27, 2013 at 03:59:56PM +0100, Hans de Goede wrote:
>> The sun4i resisitive touchscreen controller also comes with a built-in
>> temperature sensor. This commit adds support for it.
>>
>> This commit also introduces a new "ts-attached" device-tree property,
>> when this is not set, the input part of the driver won't register. This way
>> the internal temperature sensor can be used to measure the SoC temperature
>> independent of there actually being a touchscreen attached to the controller.
>>
>> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>
> Hi Hans,
>
> Almost good, except for a couple of nitpicks and one moe question.
> Assuming you fix the nitpicks and the question is not a concern,
> feel free to add
>
> Reviewed-by: Guenter Roeck <linux-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
>
> Guenter
>
>> ---
>> .../bindings/input/touchscreen/sun4i.txt | 6 +
>> drivers/input/touchscreen/sun4i-ts.c | 129 ++++++++++++++++-----
>> 2 files changed, 109 insertions(+), 26 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt b/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt
>> index e45927e..1fffd11 100644
>> --- a/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt
>> +++ b/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt
>> @@ -6,10 +6,16 @@ Required properties:
>> - reg: mmio address range of the chip
>> - interrupts: interrupt to which the chip is connected
>>
>> +Optional properties:
>> + - ts-attached: boolean set this to tell the driver that an actual touchscreen
>> + is attached and that it should register an input device,
>> + without this it only registers the builtin temperate sensor
>> +
>> Example:
>>
>> rtp: rtp@01c25000 {
>> compatible = "allwinner,sun4i-ts";
>> reg = <0x01c25000 0x100>;
>> interrupts = <29>;
>> + ts-attached;
>> };
>> diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c
>> index 10839d2..2d437f6 100644
>> --- a/drivers/input/touchscreen/sun4i-ts.c
>> +++ b/drivers/input/touchscreen/sun4i-ts.c
>> @@ -3,6 +3,9 @@
>> *
>> * Copyright (C) 2013 - 2014 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>> *
>> + * The hwmon parts are based on work by Corentin LABBE which is:
>> + * Copyright (C) 2013 Corentin LABBE <clabbe.montjoie-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>> + *
>> * This program is free software; you can redistribute it and/or modify
>> * it under the terms of the GNU General Public License as published by
>> * the Free Software Foundation; either version 2 of the License, or
>> @@ -30,6 +33,7 @@
>> */
>>
>> #include <linux/err.h>
>> +#include <linux/hwmon.h>
>> #include <linux/init.h>
>> #include <linux/input.h>
>> #include <linux/interrupt.h>
>> @@ -106,6 +110,7 @@ struct sun4i_ts_data {
>> void __iomem *base;
>> unsigned int irq;
>> bool ignore_fifo_data;
>> + int temp_data;
>> };
>>
>> static irqreturn_t sun4i_ts_irq(int irq, void *dev_id)
>> @@ -115,6 +120,12 @@ static irqreturn_t sun4i_ts_irq(int irq, void *dev_id)
>>
>> reg_val = readl(ts->base + TP_INT_FIFOS);
>>
>> + if (reg_val & TEMP_DATA_PENDING)
>> + ts->temp_data = readl(ts->base + TEMP_DATA);
>
> Is this read guaranteed to return 0 in the upper bit, or in other words is it
> guaranteed to never return 0xffffffff ? Otherwise there might be a problem with
> the implicit conversion to a negative integer.
Yes what is being read is a 12 bit unsigned value, with all the upper bits set to 0.
>
>> +
>> + if (!ts->input)
>> + goto leave;
>> +
>> if (reg_val & FIFO_DATA_PENDING) {
>> x = readl(ts->base + TP_DATA);
>> y = readl(ts->base + TP_DATA);
>> @@ -140,6 +151,7 @@ static irqreturn_t sun4i_ts_irq(int irq, void *dev_id)
>> input_sync(ts->input);
>> }
>>
>> +leave:
>> writel(reg_val, ts->base + TP_INT_FIFOS);
>>
>> return IRQ_HANDLED;
>> @@ -149,9 +161,9 @@ static int sun4i_ts_open(struct input_dev *dev)
>> {
>> struct sun4i_ts_data *ts = input_get_drvdata(dev);
>>
>> - /* Flush, set trig level to 1, enable data and up irqs */
>> - writel(DATA_IRQ_EN(1) | FIFO_TRIG(1) |FIFO_FLUSH(1) | TP_UP_IRQ_EN(1),
>> - ts->base + TP_INT_FIFOC);
>> + /* Flush, set trig level to 1, enable temp, data and up irqs */
>> + writel(TEMP_IRQ_EN(1) | DATA_IRQ_EN(1) | FIFO_TRIG(1) | FIFO_FLUSH(1) |
>> + TP_UP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
>>
>> return 0;
>> }
>> @@ -160,40 +172,76 @@ static void sun4i_ts_close(struct input_dev *dev)
>> {
>> struct sun4i_ts_data *ts = input_get_drvdata(dev);
>>
>> - /* Deactivate all IRQs */
>> - writel(0, ts->base + TP_INT_FIFOC);
>> + /* Deactivate all input IRQs */
>> + writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
>> synchronize_irq(ts->irq);
>> }
>>
>> +static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
>> + char *buf)
>> +{
>> + struct sun4i_ts_data *ts = dev_get_drvdata(dev);
>> +
>> + /* No temp_data until the first irq */
>> + if (ts->temp_data == -1)
>> + return -EAGAIN;
>> +
>> + return sprintf(buf, "%d\n", (ts->temp_data - 1447) * 100);
>> +}
>> +
>> +static ssize_t show_temp_label(struct device *dev,
>> + struct device_attribute *devattr, char *buf)
>> +{
>> + return sprintf(buf, "SoC temperature\n");
>> +}
>> +
>> +static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
>> +static DEVICE_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL);
>> +
>> +static struct attribute *sun4i_ts_attrs[] = {
>> + &dev_attr_temp1_input.attr,
>> + &dev_attr_temp1_label.attr,
>> + NULL
>> +};
>> +ATTRIBUTE_GROUPS(sun4i_ts);
>> +
>> static int sun4i_ts_probe(struct platform_device *pdev)
>> {
>> struct sun4i_ts_data *ts;
>> struct device *dev = &pdev->dev;
>> + struct device_node *np =dev->of_node;
>
> Missing space after '=' (checkpatch error).
>
>> + struct device *hwmon;
>> int ret;
>> + bool ts_attached;
>> +
>> + ts_attached = of_property_read_bool(np, "ts-attached");
>>
>> ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL);
>> if (!ts)
>> return -ENOMEM;
>>
>> ts->dev = dev;
>> -
>> - ts->input = devm_input_allocate_device(dev);
>> - if (!ts->input)
>> - return -ENOMEM;
>> -
>> - ts->input->name = pdev->name;
>> - ts->input->phys = "sun4i_ts/input0";
>> - ts->input->open = sun4i_ts_open;
>> - ts->input->close = sun4i_ts_close;
>> - ts->input->id.bustype = BUS_HOST;
>> - ts->input->id.vendor = 0x0001;
>> - ts->input->id.product = 0x0001;
>> - ts->input->id.version = 0x0100;
>> - ts->input->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
>> - set_bit(BTN_TOUCH, ts->input->keybit);
>> - input_set_abs_params(ts->input, ABS_X, 0, 4095, 0, 0);
>> - input_set_abs_params(ts->input, ABS_Y, 0, 4095, 0, 0);
>> - input_set_drvdata(ts->input, ts);
>> + ts->temp_data = -1;
>> +
>> + if (ts_attached) {
>> + ts->input = devm_input_allocate_device(dev);
>> + if (!ts->input)
>> + return -ENOMEM;
>> +
>> + ts->input->name = pdev->name;
>> + ts->input->phys = "sun4i_ts/input0";
>> + ts->input->open = sun4i_ts_open;
>> + ts->input->close = sun4i_ts_close;
>> + ts->input->id.bustype = BUS_HOST;
>> + ts->input->id.vendor = 0x0001;
>> + ts->input->id.product = 0x0001;
>> + ts->input->id.version = 0x0100;
>> + ts->input->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
>> + set_bit(BTN_TOUCH, ts->input->keybit);
>> + input_set_abs_params(ts->input, ABS_X, 0, 4095, 0, 0);
>> + input_set_abs_params(ts->input, ABS_Y, 0, 4095, 0, 0);
>> + input_set_drvdata(ts->input, ts);
>> + }
>>
>> ts->base = devm_ioremap_resource(dev,
>> platform_get_resource(pdev, IORESOURCE_MEM, 0));
>> @@ -232,14 +280,42 @@ static int sun4i_ts_probe(struct platform_device *pdev)
>> writel(STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1) | TP_MODE_EN(1),
>> ts->base + TP_CTRL1);
>>
>> - ret = input_register_device(ts->input);
>> - if (ret)
>> - return ret;
>> + hwmon = devm_hwmon_device_register_with_groups(ts->dev, "sun4i_ts",
>> + ts, sun4i_ts_groups);
>> + if (IS_ERR(hwmon)) {
>> + return PTR_ERR(hwmon);
>> + }
>
> { } are not needed here (checkpatch warning).
Oops, should have run checkpatch on v2 too, my bad. Both fixed now.
Regards,
Hans
^ permalink raw reply
* Re: [PATCH v2 2/5] input: sun4i-ts: Add support for temperature sensor
From: Hans de Goede @ 2013-12-31 11:10 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: linux-input-u79uwXL29TY76Z2rM5mHXA, LM Sensors, Maxime Ripard,
Corentin LABBE, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <20131228011007.GA14188-WlK9ik9hQGAhIp7JRqBPierSzoNAToWh@public.gmane.org>
Hi,
On 12/28/2013 02:10 AM, Dmitry Torokhov wrote:
> Hi Hans,
>
> On Fri, Dec 27, 2013 at 03:59:56PM +0100, Hans de Goede wrote:
>> +
>> + if (!ts->input)
>> + goto leave;
>> +
>> if (reg_val & FIFO_DATA_PENDING) {
>> x = readl(ts->base + TP_DATA);
>> y = readl(ts->base + TP_DATA);
>> @@ -140,6 +151,7 @@ static irqreturn_t sun4i_ts_irq(int irq, void *dev_id)
>> input_sync(ts->input);
>> }
>>
>> +leave:
>
> Can we please not introduce gotos where they are not needed? If you
> concerned about extra indent just split touchscreen handling into a
> separate function and then do
>
> if (ts->input)
> sun4i_ts_handle_touchscreen_data(...);
>
Fixed.
>
>> writel(reg_val, ts->base + TP_INT_FIFOS);
>>
>> return IRQ_HANDLED;
>> @@ -149,9 +161,9 @@ static int sun4i_ts_open(struct input_dev *dev)
>> {
>> struct sun4i_ts_data *ts = input_get_drvdata(dev);
>>
>> - /* Flush, set trig level to 1, enable data and up irqs */
>> - writel(DATA_IRQ_EN(1) | FIFO_TRIG(1) |FIFO_FLUSH(1) | TP_UP_IRQ_EN(1),
>> - ts->base + TP_INT_FIFOC);
>> + /* Flush, set trig level to 1, enable temp, data and up irqs */
>> + writel(TEMP_IRQ_EN(1) | DATA_IRQ_EN(1) | FIFO_TRIG(1) | FIFO_FLUSH(1) |
>> + TP_UP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
>>
>> return 0;
>> }
>> @@ -160,40 +172,76 @@ static void sun4i_ts_close(struct input_dev *dev)
>> {
>> struct sun4i_ts_data *ts = input_get_drvdata(dev);
>>
>> - /* Deactivate all IRQs */
>> - writel(0, ts->base + TP_INT_FIFOC);
>> + /* Deactivate all input IRQs */
>> + writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
>> synchronize_irq(ts->irq);
>
> Hmm, it would be nice we touchscreen methods only affected touchscreen
> functionality. Can you read current state and adjust it as needed
> instead of clobbering it?
I could do a read-modify-write of TP_INT_FIFOC here, but I already
considered that and I decided not to do it because ...
> Then you could do away with remove() method altogether.
No it will still be needed to clear the TEMP_IRQ_EN bit in
TP_INT_FIFOC. And it will still need to either explicitly deregister
the input_dev, or take the input_dev mutex to avoid the modification of
TP_INT_FIFOC in _remove racing with the read-modify-write in _close.
So I decided to keep this kiss, and simply force _close to run
before _remove clears the TEMP_IRQ_EN bit by doing an explicit
unregister before this.
>
>> }
>>
>> +static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
>> + char *buf)
>> +{
>> + struct sun4i_ts_data *ts = dev_get_drvdata(dev);
>> +
>> + /* No temp_data until the first irq */
>> + if (ts->temp_data == -1)
>> + return -EAGAIN;
>> +
>> + return sprintf(buf, "%d\n", (ts->temp_data - 1447) * 100);
>> +}
>> +
>> +static ssize_t show_temp_label(struct device *dev,
>> + struct device_attribute *devattr, char *buf)
>> +{
>> + return sprintf(buf, "SoC temperature\n");
>> +}
>> +
>> +static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
>> +static DEVICE_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL);
>> +
>> +static struct attribute *sun4i_ts_attrs[] = {
>> + &dev_attr_temp1_input.attr,
>> + &dev_attr_temp1_label.attr,
>> + NULL
>> +};
>> +ATTRIBUTE_GROUPS(sun4i_ts);
>> +
>> static int sun4i_ts_probe(struct platform_device *pdev)
>> {
>> struct sun4i_ts_data *ts;
>> struct device *dev = &pdev->dev;
>> + struct device_node *np =dev->of_node;
>> + struct device *hwmon;
>> int ret;
>> + bool ts_attached;
>> +
>> + ts_attached = of_property_read_bool(np, "ts-attached");
>>
>> ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL);
>> if (!ts)
>> return -ENOMEM;
>>
>> ts->dev = dev;
>> -
>> - ts->input = devm_input_allocate_device(dev);
>> - if (!ts->input)
>> - return -ENOMEM;
>> -
>> - ts->input->name = pdev->name;
>> - ts->input->phys = "sun4i_ts/input0";
>> - ts->input->open = sun4i_ts_open;
>> - ts->input->close = sun4i_ts_close;
>> - ts->input->id.bustype = BUS_HOST;
>> - ts->input->id.vendor = 0x0001;
>> - ts->input->id.product = 0x0001;
>> - ts->input->id.version = 0x0100;
>> - ts->input->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
>> - set_bit(BTN_TOUCH, ts->input->keybit);
>> - input_set_abs_params(ts->input, ABS_X, 0, 4095, 0, 0);
>> - input_set_abs_params(ts->input, ABS_Y, 0, 4095, 0, 0);
>> - input_set_drvdata(ts->input, ts);
>> + ts->temp_data = -1;
>> +
>> + if (ts_attached) {
>> + ts->input = devm_input_allocate_device(dev);
>> + if (!ts->input)
>> + return -ENOMEM;
>> +
>> + ts->input->name = pdev->name;
>> + ts->input->phys = "sun4i_ts/input0";
>> + ts->input->open = sun4i_ts_open;
>> + ts->input->close = sun4i_ts_close;
>> + ts->input->id.bustype = BUS_HOST;
>> + ts->input->id.vendor = 0x0001;
>> + ts->input->id.product = 0x0001;
>> + ts->input->id.version = 0x0100;
>> + ts->input->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
>> + set_bit(BTN_TOUCH, ts->input->keybit);
>> + input_set_abs_params(ts->input, ABS_X, 0, 4095, 0, 0);
>> + input_set_abs_params(ts->input, ABS_Y, 0, 4095, 0, 0);
>> + input_set_drvdata(ts->input, ts);
>> + }
>>
>> ts->base = devm_ioremap_resource(dev,
>> platform_get_resource(pdev, IORESOURCE_MEM, 0));
>> @@ -232,14 +280,42 @@ static int sun4i_ts_probe(struct platform_device *pdev)
>> writel(STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1) | TP_MODE_EN(1),
>> ts->base + TP_CTRL1);
>>
>> - ret = input_register_device(ts->input);
>> - if (ret)
>> - return ret;
>> + hwmon = devm_hwmon_device_register_with_groups(ts->dev, "sun4i_ts",
>> + ts, sun4i_ts_groups);
>> + if (IS_ERR(hwmon)) {
>> + return PTR_ERR(hwmon);
>> + }
>> +
>> + writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
>> +
>> + if (ts_attached) {
>> + ret = input_register_device(ts->input);
>> + if (ret) {
>> + writel(0, ts->base + TP_INT_FIFOC);
>> + synchronize_irq(ts->irq);
>
> Given that we now can't avoid freeing irq before freeing ts memory or
> input device I think you can do away with synchronize_irq() here and
> elsewhere as freeing irq is guaranteed to wait for current interrupt to
> complete.
Ah yes, removed all calls to synchronize_irq.
I'll run some tests to ensure I did not accidentally break anything and
then I'll send a v3.
>
>> + return ret;
>> + }
>> + }
>>
>> platform_set_drvdata(pdev, ts);
>> return 0;
>> }
>>
>> +static int sun4i_ts_remove(struct platform_device *pdev)
>> +{
>> + struct sun4i_ts_data *ts = platform_get_drvdata(pdev);
>> +
>> + /* Explicit unregister to avoid open/close changing the imask later */
>> + if (ts->input)
>> + input_unregister_device(ts->input);
>> +
>> + /* Deactivate all IRQs */
>> + writel(0, ts->base + TP_INT_FIFOC);
>> + synchronize_irq(ts->irq);
>> +
>> + return 0;
>> +}
>> +
>> static const struct of_device_id sun4i_ts_of_match[] = {
>> { .compatible = "allwinner,sun4i-ts", },
>> { /* sentinel */ }
>> @@ -253,6 +329,7 @@ static struct platform_driver sun4i_ts_driver = {
>> .of_match_table = of_match_ptr(sun4i_ts_of_match),
>> },
>> .probe = sun4i_ts_probe,
>> + .remove = sun4i_ts_remove,
>> };
>>
>> module_platform_driver(sun4i_ts_driver);
>> --
>> 1.8.4.2
>>
>
> Thanks.
>
Regards,
Hans
^ permalink raw reply
* Re: [PATCH v2 2/5] input: sun4i-ts: Add support for temperature sensor
From: Hans de Goede @ 2013-12-31 11:17 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: linux-input-u79uwXL29TY76Z2rM5mHXA, LM Sensors, Maxime Ripard,
Corentin LABBE, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <20131228011304.GB14188-WlK9ik9hQGAhIp7JRqBPierSzoNAToWh@public.gmane.org>
Hi,
On 12/28/2013 02:13 AM, Dmitry Torokhov wrote:
> On Fri, Dec 27, 2013 at 03:59:56PM +0100, Hans de Goede wrote:
>>
>> +Optional properties:
>> + - ts-attached: boolean set this to tell the driver that an actual touchscreen
>> + is attached and that it should register an input device,
>> + without this it only registers the builtin temperate sensor
>
> I believe the guidance is to avoid describing kernel behavior in DT
> property so I think it should say:
>
> - ts-attached: boolean indicating that an actual touchscreen is
> attached to the controller.
Will also fix for v3.
Regards,
Hans
^ permalink raw reply
* Re: [PATCH v2 2/5] input: sun4i-ts: Add support for temperature sensor
From: Maxime Ripard @ 2013-12-31 13:00 UTC (permalink / raw)
To: Hans de Goede
Cc: Dmitry Torokhov, linux-input-u79uwXL29TY76Z2rM5mHXA, LM Sensors,
Corentin LABBE, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <20131228011304.GB14188-WlK9ik9hQGAhIp7JRqBPierSzoNAToWh@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 808 bytes --]
Hi,
On Fri, Dec 27, 2013 at 05:13:04PM -0800, Dmitry Torokhov wrote:
> On Fri, Dec 27, 2013 at 03:59:56PM +0100, Hans de Goede wrote:
> >
> > +Optional properties:
> > + - ts-attached: boolean set this to tell the driver that an actual touchscreen
> > + is attached and that it should register an input device,
> > + without this it only registers the builtin temperate sensor
>
> I believe the guidance is to avoid describing kernel behavior in DT
> property so I think it should say:
>
> - ts-attached: boolean indicating that an actual touchscreen is
> attached to the controller.
Custom properties are also supposed to be prefixed by the vendor prefix.
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [Xen-devel] [PATCH v3 1/2] xen/pvhvm: If xen_platform_pci=0 is set don't blow up (v3).
From: Konrad Rzeszutek Wilk @ 2013-12-31 14:32 UTC (permalink / raw)
To: Fabio Fantoni
Cc: axboe, stefano.stabellini, ian.campbell, xen-devel, linux-kernel,
boris.ostrovsky, david.vrabel, leosilva, ashley, peterhuewe, mail,
tpmdd, dmitry.torokhov, bhelgaas, plagnioj, tomi.valkeinen,
tpmdd-devel, linux-input, netdev, linux-pci, linux-fbdev
In-Reply-To: <20131217212333.GA31966@phenom.dumpdata.com>
> That is because 'disks' is incorrect. It should have been 'ide-disks'
>
> [ 0.000000] unrecognised option 'disks' in parameter 'xen_emul_unplug'
>
> With the 'ide-disks' it should work. I will update the description to
> mention 'ide-disks' instead of 'disks'. Thank you for finding this!
>
I've v4 with said update and will push it to Linus shortly.
Thanks!
P.S.
Here is v4:
>From 275a81e7496d3532e5b4752703c50a7c8355a6c7 Mon Sep 17 00:00:00 2001
From: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Date: Tue, 26 Nov 2013 15:05:40 -0500
Subject: [PATCH] xen/pvhvm: If xen_platform_pci=0 is set don't blow up (v4).
The user has the option of disabling the platform driver:
00:02.0 Unassigned class [ff80]: XenSource, Inc. Xen Platform Device (rev 01)
which is used to unplug the emulated drivers (IDE, Realtek 8169, etc)
and allow the PV drivers to take over. If the user wishes
to disable that they can set:
xen_platform_pci=0
(in the guest config file)
or
xen_emul_unplug=never
(on the Linux command line)
except it does not work properly. The PV drivers still try to
load and since the Xen platform driver is not run - and it
has not initialized the grant tables, most of the PV drivers
stumble upon:
input: Xen Virtual Keyboard as /devices/virtual/input/input5
input: Xen Virtual Pointer as /devices/virtual/input/input6M
------------[ cut here ]------------
kernel BUG at /home/konrad/ssd/konrad/linux/drivers/xen/grant-table.c:1206!
invalid opcode: 0000 [#1] SMP
Modules linked in: xen_kbdfront(+) xenfs xen_privcmd
CPU: 6 PID: 1389 Comm: modprobe Not tainted 3.13.0-rc1upstream-00021-ga6c892b-dirty #1
Hardware name: Xen HVM domU, BIOS 4.4-unstable 11/26/2013
RIP: 0010:[<ffffffff813ddc40>] [<ffffffff813ddc40>] get_free_entries+0x2e0/0x300
Call Trace:
[<ffffffff8150d9a3>] ? evdev_connect+0x1e3/0x240
[<ffffffff813ddd0e>] gnttab_grant_foreign_access+0x2e/0x70
[<ffffffffa0010081>] xenkbd_connect_backend+0x41/0x290 [xen_kbdfront]
[<ffffffffa0010a12>] xenkbd_probe+0x2f2/0x324 [xen_kbdfront]
[<ffffffff813e5757>] xenbus_dev_probe+0x77/0x130
[<ffffffff813e7217>] xenbus_frontend_dev_probe+0x47/0x50
[<ffffffff8145e9a9>] driver_probe_device+0x89/0x230
[<ffffffff8145ebeb>] __driver_attach+0x9b/0xa0
[<ffffffff8145eb50>] ? driver_probe_device+0x230/0x230
[<ffffffff8145eb50>] ? driver_probe_device+0x230/0x230
[<ffffffff8145cf1c>] bus_for_each_dev+0x8c/0xb0
[<ffffffff8145e7d9>] driver_attach+0x19/0x20
[<ffffffff8145e260>] bus_add_driver+0x1a0/0x220
[<ffffffff8145f1ff>] driver_register+0x5f/0xf0
[<ffffffff813e55c5>] xenbus_register_driver_common+0x15/0x20
[<ffffffff813e76b3>] xenbus_register_frontend+0x23/0x40
[<ffffffffa0015000>] ? 0xffffffffa0014fff
[<ffffffffa001502b>] xenkbd_init+0x2b/0x1000 [xen_kbdfront]
[<ffffffff81002049>] do_one_initcall+0x49/0x170
.. snip..
which is hardly nice. This patch fixes this by having each
PV driver check for:
- if running in PV, then it is fine to execute (as that is their
native environment).
- if running in HVM, check if user wanted 'xen_emul_unplug=never',
in which case bail out and don't load any PV drivers.
- if running in HVM, and if PCI device 5853:0001 (xen_platform_pci)
does not exist, then bail out and not load PV drivers.
- (v2) if running in HVM, and if the user wanted 'xen_emul_unplug=ide-disks',
then bail out for all PV devices _except_ the block one.
Ditto for the network one ('nics').
- (v2) if running in HVM, and if the user wanted 'xen_emul_unplug=unnecessary'
then load block PV driver, and also setup the legacy IDE paths.
In (v3) make it actually load PV drivers.
Reported-by: Sander Eikelenboom <linux@eikelenboom.it
Reported-by: Anthony PERARD <anthony.perard@citrix.com>
Reported-and-Tested-by: Fabio Fantoni <fabio.fantoni@m2r.biz>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
[v2: Add extra logic to handle the myrid ways 'xen_emul_unplug'
can be used per Ian and Stefano suggestion]
[v3: Make the unnecessary case work properly]
[v4: s/disks/ide-disks/ spotted by Fabio]
Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com> [for PCI parts]
---
arch/x86/xen/platform-pci-unplug.c | 74 ++++++++++++++++++++++++++++++
drivers/block/xen-blkfront.c | 4 +-
drivers/char/tpm/xen-tpmfront.c | 4 ++
drivers/input/misc/xen-kbdfront.c | 4 ++
drivers/net/xen-netfront.c | 2 +-
drivers/pci/xen-pcifront.c | 4 ++
drivers/video/xen-fbfront.c | 4 ++
drivers/xen/xenbus/xenbus_probe_frontend.c | 2 +-
include/xen/platform_pci.h | 23 ++++++++++
9 files changed, 117 insertions(+), 4 deletions(-)
diff --git a/arch/x86/xen/platform-pci-unplug.c b/arch/x86/xen/platform-pci-unplug.c
index 0a78524..ab84ac1 100644
--- a/arch/x86/xen/platform-pci-unplug.c
+++ b/arch/x86/xen/platform-pci-unplug.c
@@ -69,6 +69,80 @@ static int check_platform_magic(void)
return 0;
}
+bool xen_has_pv_devices()
+{
+ if (!xen_domain())
+ return false;
+
+ /* PV domains always have them. */
+ if (xen_pv_domain())
+ return true;
+
+ /* And user has xen_platform_pci=0 set in guest config as
+ * driver did not modify the value. */
+ if (xen_platform_pci_unplug == 0)
+ return false;
+
+ if (xen_platform_pci_unplug & XEN_UNPLUG_NEVER)
+ return false;
+
+ if (xen_platform_pci_unplug & XEN_UNPLUG_ALL)
+ return true;
+
+ /* This is an odd one - we are going to run legacy
+ * and PV drivers at the same time. */
+ if (xen_platform_pci_unplug & XEN_UNPLUG_UNNECESSARY)
+ return true;
+
+ /* And the caller has to follow with xen_pv_{disk,nic}_devices
+ * to be certain which driver can load. */
+ return false;
+}
+EXPORT_SYMBOL_GPL(xen_has_pv_devices);
+
+static bool __xen_has_pv_device(int state)
+{
+ /* HVM domains might or might not */
+ if (xen_hvm_domain() && (xen_platform_pci_unplug & state))
+ return true;
+
+ return xen_has_pv_devices();
+}
+
+bool xen_has_pv_nic_devices(void)
+{
+ return __xen_has_pv_device(XEN_UNPLUG_ALL_NICS | XEN_UNPLUG_ALL);
+}
+EXPORT_SYMBOL_GPL(xen_has_pv_nic_devices);
+
+bool xen_has_pv_disk_devices(void)
+{
+ return __xen_has_pv_device(XEN_UNPLUG_ALL_IDE_DISKS |
+ XEN_UNPLUG_AUX_IDE_DISKS | XEN_UNPLUG_ALL);
+}
+EXPORT_SYMBOL_GPL(xen_has_pv_disk_devices);
+
+/*
+ * This one is odd - it determines whether you want to run PV _and_
+ * legacy (IDE) drivers together. This combination is only possible
+ * under HVM.
+ */
+bool xen_has_pv_and_legacy_disk_devices(void)
+{
+ if (!xen_domain())
+ return false;
+
+ /* N.B. This is only ever used in HVM mode */
+ if (xen_pv_domain())
+ return false;
+
+ if (xen_platform_pci_unplug & XEN_UNPLUG_UNNECESSARY)
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(xen_has_pv_and_legacy_disk_devices);
+
void xen_unplug_emulated_devices(void)
{
int r;
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index a4660bb..ed88b3c 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -1278,7 +1278,7 @@ static int blkfront_probe(struct xenbus_device *dev,
char *type;
int len;
/* no unplug has been done: do not hook devices != xen vbds */
- if (xen_platform_pci_unplug & XEN_UNPLUG_UNNECESSARY) {
+ if (xen_has_pv_and_legacy_disk_devices()) {
int major;
if (!VDEV_IS_EXTENDED(vdevice))
@@ -2022,7 +2022,7 @@ static int __init xlblk_init(void)
if (!xen_domain())
return -ENODEV;
- if (xen_hvm_domain() && !xen_platform_pci_unplug)
+ if (!xen_has_pv_disk_devices())
return -ENODEV;
if (register_blkdev(XENVBD_MAJOR, DEV_NAME)) {
diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c
index 06189e5..9c2cbd1 100644
--- a/drivers/char/tpm/xen-tpmfront.c
+++ b/drivers/char/tpm/xen-tpmfront.c
@@ -16,6 +16,7 @@
#include <xen/xenbus.h>
#include <xen/page.h>
#include "tpm.h"
+#include <xen/platform_pci.h>
struct tpm_private {
struct tpm_chip *chip;
@@ -422,6 +423,9 @@ static int __init xen_tpmfront_init(void)
if (!xen_domain())
return -ENODEV;
+ if (!xen_has_pv_devices())
+ return -ENODEV;
+
return xenbus_register_frontend(&tpmfront_driver);
}
module_init(xen_tpmfront_init);
diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c
index e21c181..fbfdc10 100644
--- a/drivers/input/misc/xen-kbdfront.c
+++ b/drivers/input/misc/xen-kbdfront.c
@@ -29,6 +29,7 @@
#include <xen/interface/io/fbif.h>
#include <xen/interface/io/kbdif.h>
#include <xen/xenbus.h>
+#include <xen/platform_pci.h>
struct xenkbd_info {
struct input_dev *kbd;
@@ -380,6 +381,9 @@ static int __init xenkbd_init(void)
if (xen_initial_domain())
return -ENODEV;
+ if (!xen_has_pv_devices())
+ return -ENODEV;
+
return xenbus_register_frontend(&xenkbd_driver);
}
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index 36808bf..eea2392 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -2106,7 +2106,7 @@ static int __init netif_init(void)
if (!xen_domain())
return -ENODEV;
- if (xen_hvm_domain() && !xen_platform_pci_unplug)
+ if (!xen_has_pv_nic_devices())
return -ENODEV;
pr_info("Initialising Xen virtual ethernet driver\n");
diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c
index f7197a7..eae7cd9 100644
--- a/drivers/pci/xen-pcifront.c
+++ b/drivers/pci/xen-pcifront.c
@@ -20,6 +20,7 @@
#include <linux/workqueue.h>
#include <linux/bitops.h>
#include <linux/time.h>
+#include <xen/platform_pci.h>
#include <asm/xen/swiotlb-xen.h>
#define INVALID_GRANT_REF (0)
@@ -1138,6 +1139,9 @@ static int __init pcifront_init(void)
if (!xen_pv_domain() || xen_initial_domain())
return -ENODEV;
+ if (!xen_has_pv_devices())
+ return -ENODEV;
+
pci_frontend_registrar(1 /* enable */);
return xenbus_register_frontend(&xenpci_driver);
diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c
index cd005c2..4b2d3ab 100644
--- a/drivers/video/xen-fbfront.c
+++ b/drivers/video/xen-fbfront.c
@@ -35,6 +35,7 @@
#include <xen/interface/io/fbif.h>
#include <xen/interface/io/protocols.h>
#include <xen/xenbus.h>
+#include <xen/platform_pci.h>
struct xenfb_info {
unsigned char *fb;
@@ -699,6 +700,9 @@ static int __init xenfb_init(void)
if (xen_initial_domain())
return -ENODEV;
+ if (!xen_has_pv_devices())
+ return -ENODEV;
+
return xenbus_register_frontend(&xenfb_driver);
}
diff --git a/drivers/xen/xenbus/xenbus_probe_frontend.c b/drivers/xen/xenbus/xenbus_probe_frontend.c
index 34b20bf..6244f9c 100644
--- a/drivers/xen/xenbus/xenbus_probe_frontend.c
+++ b/drivers/xen/xenbus/xenbus_probe_frontend.c
@@ -496,7 +496,7 @@ subsys_initcall(xenbus_probe_frontend_init);
#ifndef MODULE
static int __init boot_wait_for_devices(void)
{
- if (xen_hvm_domain() && !xen_platform_pci_unplug)
+ if (!xen_has_pv_devices())
return -ENODEV;
ready_to_wait_for_devices = 1;
diff --git a/include/xen/platform_pci.h b/include/xen/platform_pci.h
index 438c256..b49eeab 100644
--- a/include/xen/platform_pci.h
+++ b/include/xen/platform_pci.h
@@ -48,4 +48,27 @@ static inline int xen_must_unplug_disks(void) {
extern int xen_platform_pci_unplug;
+#if defined(CONFIG_XEN_PVHVM)
+extern bool xen_has_pv_devices(void);
+extern bool xen_has_pv_disk_devices(void);
+extern bool xen_has_pv_nic_devices(void);
+extern bool xen_has_pv_and_legacy_disk_devices(void);
+#else
+static inline bool xen_has_pv_devices(void)
+{
+ return IS_ENABLED(CONFIG_XEN);
+}
+static inline bool xen_has_pv_disk_devices(void)
+{
+ return IS_ENABLED(CONFIG_XEN);
+}
+static inline bool xen_has_pv_nic_devices(void)
+{
+ return IS_ENABLED(CONFIG_XEN);
+}
+static inline bool xen_has_pv_and_legacy_disk_devices(void)
+{
+ return false;
+}
+#endif
#endif /* _XEN_PLATFORM_PCI_H */
--
1.8.3.1
^ permalink raw reply related
* Re: [PATCH v2 2/5] input: sun4i-ts: Add support for temperature sensor
From: Hans de Goede @ 2013-12-31 15:02 UTC (permalink / raw)
To: Maxime Ripard
Cc: Dmitry Torokhov, linux-input-u79uwXL29TY76Z2rM5mHXA, LM Sensors,
Corentin LABBE, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <20131231130006.GW3144@lukather>
Hi,
On 12/31/2013 02:00 PM, Maxime Ripard wrote:
> Hi,
>
> On Fri, Dec 27, 2013 at 05:13:04PM -0800, Dmitry Torokhov wrote:
>> On Fri, Dec 27, 2013 at 03:59:56PM +0100, Hans de Goede wrote:
>>>
>>> +Optional properties:
>>> + - ts-attached: boolean set this to tell the driver that an actual touchscreen
>>> + is attached and that it should register an input device,
>>> + without this it only registers the builtin temperate sensor
>>
>> I believe the guidance is to avoid describing kernel behavior in DT
>> property so I think it should say:
>>
>> - ts-attached: boolean indicating that an actual touchscreen is
>> attached to the controller.
>
> Custom properties are also supposed to be prefixed by the vendor prefix.
Will fix for v3.
Regards,
Hans
^ permalink raw reply
* [PATCH v3 0/5] input: Add new sun4i-ts driver for Allwinner sunxi
From: Hans de Goede @ 2013-12-31 16:20 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: linux-input-u79uwXL29TY76Z2rM5mHXA, LM Sensors, Maxime Ripard,
Corentin LABBE, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
Hi All,
Here is v3 of my sun4i-ts driver for Allwinner sunxi patch-set.
Changed this version:
-Addressed review comments from Dimitri
-Addressed review comments from Guenter
-Addressed review comments from Maxime
-Initialize ignore_fifo_data to true, the very first coordinate can be
wrong too
Thanks & Regards,
Hans
^ permalink raw reply
* [PATCH v3 1/5] input: Add new sun4i-ts driver for Allwinner sunxi SoC's rtp controller
From: Hans de Goede @ 2013-12-31 16:20 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: linux-input-u79uwXL29TY76Z2rM5mHXA, LM Sensors, Maxime Ripard,
Corentin LABBE, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Hans de Goede
In-Reply-To: <1388506852-3548-1-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Note the sun4i-ts controller is capable of detecting a second touch, but when
a second touch is present then the accuracy becomes so bad the reported touch
location is not useable.
The original android driver contains some complicated heuristics using the
aprox. distance between the 2 touches to see if the user is making a pinch
open / close movement, and then reports emulated multi-touch events around
the last touch coordinate (as the dual-touch coordinates are worthless).
These kinds of heuristics are just asking for trouble (and don't belong
in the kernel). So this driver offers straight forward, reliable single
touch functionality only.
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
.../bindings/input/touchscreen/sun4i.txt | 15 ++
drivers/input/touchscreen/Kconfig | 10 +
drivers/input/touchscreen/Makefile | 1 +
drivers/input/touchscreen/sun4i-ts.c | 262 +++++++++++++++++++++
4 files changed, 288 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/touchscreen/sun4i.txt
create mode 100644 drivers/input/touchscreen/sun4i-ts.c
diff --git a/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt b/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt
new file mode 100644
index 0000000..e45927e
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt
@@ -0,0 +1,15 @@
+sun4i resistive touchscreen controller
+--------------------------------------
+
+Required properties:
+ - compatible: "allwinner,sun4i-ts"
+ - reg: mmio address range of the chip
+ - interrupts: interrupt to which the chip is connected
+
+Example:
+
+ rtp: rtp@01c25000 {
+ compatible = "allwinner,sun4i-ts";
+ reg = <0x01c25000 0x100>;
+ interrupts = <29>;
+ };
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 961d58d..95023de 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -906,6 +906,16 @@ config TOUCHSCREEN_STMPE
To compile this driver as a module, choose M here: the
module will be called stmpe-ts.
+config TOUCHSCREEN_SUN4I
+ tristate "Allwinner sun4i resistive touchscreen controller support"
+ depends on ARCH_SUNXI
+ help
+ This selects support for the resistive touchscreen controller
+ found on Allwinner sunxi SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sun4i-ts.
+
config TOUCHSCREEN_SUR40
tristate "Samsung SUR40 (Surface 2.0/PixelSense) touchscreen"
depends on USB
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 62801f2..c8f7375 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_PIXCIR) += pixcir_i2c_ts.o
obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o
obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o
+obj-$(CONFIG_TOUCHSCREEN_SUN4I) += sun4i-ts.o
obj-$(CONFIG_TOUCHSCREEN_SUR40) += sur40.o
obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o
obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o
diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c
new file mode 100644
index 0000000..5945219
--- /dev/null
+++ b/drivers/input/touchscreen/sun4i-ts.c
@@ -0,0 +1,262 @@
+/*
+ * Allwinner sunxi resistive touchscreen controller driver
+ *
+ * Copyright (C) 2013 - 2014 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * The sun4i-ts controller is capable of detecting a second touch, but when a
+ * second touch is present then the accuracy becomes so bad the reported touch
+ * location is not useable.
+ *
+ * The original android driver contains some complicated heuristics using the
+ * aprox. distance between the 2 touches to see if the user is making a pinch
+ * open / close movement, and then reports emulated multi-touch events around
+ * the last touch coordinate (as the dual-touch coordinates are worthless).
+ *
+ * These kinds of heuristics are just asking for trouble (and don't belong
+ * in the kernel). So this driver offers straight forward, reliable single
+ * touch functionality only.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define TP_CTRL0 0x00
+#define TP_CTRL1 0x04
+#define TP_CTRL2 0x08
+#define TP_CTRL3 0x0c
+#define TP_INT_FIFOC 0x10
+#define TP_INT_FIFOS 0x14
+#define TP_TPR 0x18
+#define TP_CDAT 0x1c
+#define TEMP_DATA 0x20
+#define TP_DATA 0x24
+
+/* TP_CTRL0 bits */
+#define ADC_FIRST_DLY(x) ((x) << 24) /* 8 bits */
+#define ADC_FIRST_DLY_MODE(x) ((x) << 23)
+#define ADC_CLK_SEL(x) ((x) << 22)
+#define ADC_CLK_DIV(x) ((x) << 20) /* 3 bits */
+#define FS_DIV(x) ((x) << 16) /* 4 bits */
+#define T_ACQ(x) ((x) << 0) /* 16 bits */
+
+/* TP_CTRL1 bits */
+#define STYLUS_UP_DEBOUN(x) ((x) << 12) /* 8 bits */
+#define STYLUS_UP_DEBOUN_EN(x) ((x) << 9)
+#define TOUCH_PAN_CALI_EN(x) ((x) << 6)
+#define TP_DUAL_EN(x) ((x) << 5)
+#define TP_MODE_EN(x) ((x) << 4)
+#define TP_ADC_SELECT(x) ((x) << 3)
+#define ADC_CHAN_SELECT(x) ((x) << 0) /* 3 bits */
+
+/* TP_CTRL2 bits */
+#define TP_SENSITIVE_ADJUST(x) ((x) << 28) /* 4 bits */
+#define TP_MODE_SELECT(x) ((x) << 26) /* 2 bits */
+#define PRE_MEA_EN(x) ((x) << 24)
+#define PRE_MEA_THRE_CNT(x) ((x) << 0) /* 24 bits */
+
+/* TP_CTRL3 bits */
+#define FILTER_EN(x) ((x) << 2)
+#define FILTER_TYPE(x) ((x) << 0) /* 2 bits */
+
+/* TP_INT_FIFOC irq and fifo mask / control bits */
+#define TEMP_IRQ_EN(x) ((x) << 18)
+#define OVERRUN_IRQ_EN(x) ((x) << 17)
+#define DATA_IRQ_EN(x) ((x) << 16)
+#define TP_DATA_XY_CHANGE(x) ((x) << 13)
+#define FIFO_TRIG(x) ((x) << 8) /* 5 bits */
+#define DATA_DRQ_EN(x) ((x) << 7)
+#define FIFO_FLUSH(x) ((x) << 4)
+#define TP_UP_IRQ_EN(x) ((x) << 1)
+#define TP_DOWN_IRQ_EN(x) ((x) << 0)
+
+/* TP_INT_FIFOS irq and fifo status bits */
+#define TEMP_DATA_PENDING BIT(18)
+#define FIFO_OVERRUN_PENDING BIT(17)
+#define FIFO_DATA_PENDING BIT(16)
+#define TP_IDLE_FLG BIT(2)
+#define TP_UP_PENDING BIT(1)
+#define TP_DOWN_PENDING BIT(0)
+
+/* TP_TPR bits */
+#define TEMP_ENABLE(x) ((x) << 16)
+#define TEMP_PERIOD(x) ((x) << 0) /* t = x * 256 * 16 / clkin */
+
+struct sun4i_ts_data {
+ struct device *dev;
+ struct input_dev *input;
+ void __iomem *base;
+ unsigned int irq;
+ bool ignore_fifo_data;
+};
+
+static irqreturn_t sun4i_ts_irq(int irq, void *dev_id)
+{
+ struct sun4i_ts_data *ts = dev_id;
+ u32 reg_val, x, y;
+
+ reg_val = readl(ts->base + TP_INT_FIFOS);
+
+ if (reg_val & FIFO_DATA_PENDING) {
+ x = readl(ts->base + TP_DATA);
+ y = readl(ts->base + TP_DATA);
+ /* The 1st location reported after an up event is unreliable */
+ if (!ts->ignore_fifo_data) {
+ input_report_abs(ts->input, ABS_X, x);
+ input_report_abs(ts->input, ABS_Y, y);
+ /*
+ * The hardware has a separate down status bit, but
+ * that gets set before we get the first location,
+ * resulting in reporting a click on the old location.
+ */
+ input_report_key(ts->input, BTN_TOUCH, 1);
+ input_sync(ts->input);
+ } else {
+ ts->ignore_fifo_data = false;
+ }
+ }
+
+ if (reg_val & TP_UP_PENDING) {
+ ts->ignore_fifo_data = true;
+ input_report_key(ts->input, BTN_TOUCH, 0);
+ input_sync(ts->input);
+ }
+
+ writel(reg_val, ts->base + TP_INT_FIFOS);
+
+ return IRQ_HANDLED;
+}
+
+static int sun4i_ts_open(struct input_dev *dev)
+{
+ struct sun4i_ts_data *ts = input_get_drvdata(dev);
+
+ /* Flush, set trig level to 1, enable data and up irqs */
+ writel(DATA_IRQ_EN(1) | FIFO_TRIG(1) | FIFO_FLUSH(1) | TP_UP_IRQ_EN(1),
+ ts->base + TP_INT_FIFOC);
+
+ return 0;
+}
+
+static void sun4i_ts_close(struct input_dev *dev)
+{
+ struct sun4i_ts_data *ts = input_get_drvdata(dev);
+
+ /* Deactivate all IRQs */
+ writel(0, ts->base + TP_INT_FIFOC);
+}
+
+static int sun4i_ts_probe(struct platform_device *pdev)
+{
+ struct sun4i_ts_data *ts;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ ts->dev = dev;
+ ts->ignore_fifo_data = true;
+
+ ts->input = devm_input_allocate_device(dev);
+ if (!ts->input)
+ return -ENOMEM;
+
+ ts->input->name = pdev->name;
+ ts->input->phys = "sun4i_ts/input0";
+ ts->input->open = sun4i_ts_open;
+ ts->input->close = sun4i_ts_close;
+ ts->input->id.bustype = BUS_HOST;
+ ts->input->id.vendor = 0x0001;
+ ts->input->id.product = 0x0001;
+ ts->input->id.version = 0x0100;
+ ts->input->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
+ set_bit(BTN_TOUCH, ts->input->keybit);
+ input_set_abs_params(ts->input, ABS_X, 0, 4095, 0, 0);
+ input_set_abs_params(ts->input, ABS_Y, 0, 4095, 0, 0);
+ input_set_drvdata(ts->input, ts);
+
+ ts->base = devm_ioremap_resource(dev,
+ platform_get_resource(pdev, IORESOURCE_MEM, 0));
+ if (IS_ERR(ts->base))
+ return PTR_ERR(ts->base);
+
+ ts->irq = platform_get_irq(pdev, 0);
+ ret = devm_request_irq(dev, ts->irq, sun4i_ts_irq, 0, "sun4i-ts", ts);
+ if (ret)
+ return ret;
+
+ /*
+ * Select HOSC clk, clkin = clk / 6, adc samplefreq = clkin / 8192,
+ * t_acq = clkin / (16 * 64)
+ */
+ writel(ADC_CLK_SEL(0) | ADC_CLK_DIV(2) | FS_DIV(7) | T_ACQ(63),
+ ts->base + TP_CTRL0);
+
+ /*
+ * sensitive_adjust = 15 : max, which is not all that sensitive,
+ * tp_mode = 0 : only x and y coordinates, as we don't use dual touch
+ */
+ writel(TP_SENSITIVE_ADJUST(15) | TP_MODE_SELECT(0),
+ ts->base + TP_CTRL2);
+
+ /* Enable median filter, type 1 : 5/3 */
+ writel(FILTER_EN(1) | FILTER_TYPE(1), ts->base + TP_CTRL3);
+
+ /* Enable temperature measurement, period 1953 (2 seconds) */
+ writel(TEMP_ENABLE(1) | TEMP_PERIOD(1953), ts->base + TP_TPR);
+
+ /*
+ * Set stylus up debounce to aprox 10 ms, enable debounce, and
+ * finally enable tp mode.
+ */
+ writel(STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1) | TP_MODE_EN(1),
+ ts->base + TP_CTRL1);
+
+ ret = input_register_device(ts->input);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, ts);
+ return 0;
+}
+
+static const struct of_device_id sun4i_ts_of_match[] = {
+ { .compatible = "allwinner,sun4i-ts", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sun4i_ts_of_match);
+
+static struct platform_driver sun4i_ts_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "sun4i-ts",
+ .of_match_table = of_match_ptr(sun4i_ts_of_match),
+ },
+ .probe = sun4i_ts_probe,
+};
+
+module_platform_driver(sun4i_ts_driver);
+
+MODULE_DESCRIPTION("Allwinner sun4i resistive touchscreen controller driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>");
+MODULE_LICENSE("GPL");
--
1.8.4.2
^ permalink raw reply related
* [PATCH v3 2/5] input: sun4i-ts: Add support for temperature sensor
From: Hans de Goede @ 2013-12-31 16:20 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: linux-input-u79uwXL29TY76Z2rM5mHXA, LM Sensors, Maxime Ripard,
Corentin LABBE, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Hans de Goede
In-Reply-To: <1388506852-3548-1-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
The sun4i resisitive touchscreen controller also comes with a built-in
temperature sensor. This commit adds support for it.
This commit also introduces a new "ts-attached" device-tree property,
when this is not set, the input part of the driver won't register. This way
the internal temperature sensor can be used to measure the SoC temperature
independent of there actually being a touchscreen attached to the controller.
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
.../bindings/input/touchscreen/sun4i.txt | 5 +
drivers/input/touchscreen/sun4i-ts.c | 140 ++++++++++++++++-----
2 files changed, 114 insertions(+), 31 deletions(-)
diff --git a/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt b/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt
index e45927e..6bac67b 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt
@@ -6,10 +6,15 @@ Required properties:
- reg: mmio address range of the chip
- interrupts: interrupt to which the chip is connected
+Optional properties:
+ - allwinner,ts-attached: boolean indicating that an actual touchscreen is
+ attached to the controller
+
Example:
rtp: rtp@01c25000 {
compatible = "allwinner,sun4i-ts";
reg = <0x01c25000 0x100>;
interrupts = <29>;
+ allwinner,ts-attached;
};
diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c
index 5945219..16cbb01 100644
--- a/drivers/input/touchscreen/sun4i-ts.c
+++ b/drivers/input/touchscreen/sun4i-ts.c
@@ -3,6 +3,9 @@
*
* Copyright (C) 2013 - 2014 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
*
+ * The hwmon parts are based on work by Corentin LABBE which is:
+ * Copyright (C) 2013 Corentin LABBE <clabbe.montjoie-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -30,6 +33,7 @@
*/
#include <linux/err.h>
+#include <linux/hwmon.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
@@ -106,14 +110,12 @@ struct sun4i_ts_data {
void __iomem *base;
unsigned int irq;
bool ignore_fifo_data;
+ int temp_data;
};
-static irqreturn_t sun4i_ts_irq(int irq, void *dev_id)
+static void sun4i_ts_irq_handle_input(struct sun4i_ts_data *ts, u32 reg_val)
{
- struct sun4i_ts_data *ts = dev_id;
- u32 reg_val, x, y;
-
- reg_val = readl(ts->base + TP_INT_FIFOS);
+ u32 x, y;
if (reg_val & FIFO_DATA_PENDING) {
x = readl(ts->base + TP_DATA);
@@ -139,6 +141,20 @@ static irqreturn_t sun4i_ts_irq(int irq, void *dev_id)
input_report_key(ts->input, BTN_TOUCH, 0);
input_sync(ts->input);
}
+}
+
+static irqreturn_t sun4i_ts_irq(int irq, void *dev_id)
+{
+ struct sun4i_ts_data *ts = dev_id;
+ u32 reg_val;
+
+ reg_val = readl(ts->base + TP_INT_FIFOS);
+
+ if (reg_val & TEMP_DATA_PENDING)
+ ts->temp_data = readl(ts->base + TEMP_DATA);
+
+ if (ts->input)
+ sun4i_ts_irq_handle_input(ts, reg_val);
writel(reg_val, ts->base + TP_INT_FIFOS);
@@ -149,9 +165,9 @@ static int sun4i_ts_open(struct input_dev *dev)
{
struct sun4i_ts_data *ts = input_get_drvdata(dev);
- /* Flush, set trig level to 1, enable data and up irqs */
- writel(DATA_IRQ_EN(1) | FIFO_TRIG(1) | FIFO_FLUSH(1) | TP_UP_IRQ_EN(1),
- ts->base + TP_INT_FIFOC);
+ /* Flush, set trig level to 1, enable temp, data and up irqs */
+ writel(TEMP_IRQ_EN(1) | DATA_IRQ_EN(1) | FIFO_TRIG(1) | FIFO_FLUSH(1) |
+ TP_UP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
return 0;
}
@@ -160,15 +176,48 @@ static void sun4i_ts_close(struct input_dev *dev)
{
struct sun4i_ts_data *ts = input_get_drvdata(dev);
- /* Deactivate all IRQs */
- writel(0, ts->base + TP_INT_FIFOC);
+ /* Deactivate all input IRQs */
+ writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
+}
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct sun4i_ts_data *ts = dev_get_drvdata(dev);
+
+ /* No temp_data until the first irq */
+ if (ts->temp_data == -1)
+ return -EAGAIN;
+
+ return sprintf(buf, "%d\n", (ts->temp_data - 1447) * 100);
+}
+
+static ssize_t show_temp_label(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "SoC temperature\n");
}
+static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
+static DEVICE_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL);
+
+static struct attribute *sun4i_ts_attrs[] = {
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_label.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(sun4i_ts);
+
static int sun4i_ts_probe(struct platform_device *pdev)
{
struct sun4i_ts_data *ts;
struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct device *hwmon;
int ret;
+ bool ts_attached;
+
+ ts_attached = of_property_read_bool(np, "allwinner,ts-attached");
ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL);
if (!ts)
@@ -176,24 +225,27 @@ static int sun4i_ts_probe(struct platform_device *pdev)
ts->dev = dev;
ts->ignore_fifo_data = true;
-
- ts->input = devm_input_allocate_device(dev);
- if (!ts->input)
- return -ENOMEM;
-
- ts->input->name = pdev->name;
- ts->input->phys = "sun4i_ts/input0";
- ts->input->open = sun4i_ts_open;
- ts->input->close = sun4i_ts_close;
- ts->input->id.bustype = BUS_HOST;
- ts->input->id.vendor = 0x0001;
- ts->input->id.product = 0x0001;
- ts->input->id.version = 0x0100;
- ts->input->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
- set_bit(BTN_TOUCH, ts->input->keybit);
- input_set_abs_params(ts->input, ABS_X, 0, 4095, 0, 0);
- input_set_abs_params(ts->input, ABS_Y, 0, 4095, 0, 0);
- input_set_drvdata(ts->input, ts);
+ ts->temp_data = -1;
+
+ if (ts_attached) {
+ ts->input = devm_input_allocate_device(dev);
+ if (!ts->input)
+ return -ENOMEM;
+
+ ts->input->name = pdev->name;
+ ts->input->phys = "sun4i_ts/input0";
+ ts->input->open = sun4i_ts_open;
+ ts->input->close = sun4i_ts_close;
+ ts->input->id.bustype = BUS_HOST;
+ ts->input->id.vendor = 0x0001;
+ ts->input->id.product = 0x0001;
+ ts->input->id.version = 0x0100;
+ ts->input->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
+ set_bit(BTN_TOUCH, ts->input->keybit);
+ input_set_abs_params(ts->input, ABS_X, 0, 4095, 0, 0);
+ input_set_abs_params(ts->input, ABS_Y, 0, 4095, 0, 0);
+ input_set_drvdata(ts->input, ts);
+ }
ts->base = devm_ioremap_resource(dev,
platform_get_resource(pdev, IORESOURCE_MEM, 0));
@@ -232,14 +284,39 @@ static int sun4i_ts_probe(struct platform_device *pdev)
writel(STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1) | TP_MODE_EN(1),
ts->base + TP_CTRL1);
- ret = input_register_device(ts->input);
- if (ret)
- return ret;
+ hwmon = devm_hwmon_device_register_with_groups(ts->dev, "sun4i_ts",
+ ts, sun4i_ts_groups);
+ if (IS_ERR(hwmon))
+ return PTR_ERR(hwmon);
+
+ writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
+
+ if (ts_attached) {
+ ret = input_register_device(ts->input);
+ if (ret) {
+ writel(0, ts->base + TP_INT_FIFOC);
+ return ret;
+ }
+ }
platform_set_drvdata(pdev, ts);
return 0;
}
+static int sun4i_ts_remove(struct platform_device *pdev)
+{
+ struct sun4i_ts_data *ts = platform_get_drvdata(pdev);
+
+ /* Explicit unregister to avoid open/close changing the imask later */
+ if (ts->input)
+ input_unregister_device(ts->input);
+
+ /* Deactivate all IRQs */
+ writel(0, ts->base + TP_INT_FIFOC);
+
+ return 0;
+}
+
static const struct of_device_id sun4i_ts_of_match[] = {
{ .compatible = "allwinner,sun4i-ts", },
{ /* sentinel */ }
@@ -253,6 +330,7 @@ static struct platform_driver sun4i_ts_driver = {
.of_match_table = of_match_ptr(sun4i_ts_of_match),
},
.probe = sun4i_ts_probe,
+ .remove = sun4i_ts_remove,
};
module_platform_driver(sun4i_ts_driver);
--
1.8.4.2
^ permalink raw reply related
* [PATCH v3 3/5] ARM: dts: sun4i: Add rtp controller node
From: Hans de Goede @ 2013-12-31 16:20 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: linux-input-u79uwXL29TY76Z2rM5mHXA, LM Sensors, Maxime Ripard,
Corentin LABBE, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Hans de Goede
In-Reply-To: <1388506852-3548-1-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
arch/arm/boot/dts/sun4i-a10.dtsi | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi
index de1dd73..502f3e2 100644
--- a/arch/arm/boot/dts/sun4i-a10.dtsi
+++ b/arch/arm/boot/dts/sun4i-a10.dtsi
@@ -452,6 +452,12 @@
reg = <0x01c23800 0x10>;
};
+ rtp: rtp@01c25000 {
+ compatible = "allwinner,sun4i-ts";
+ reg = <0x01c25000 0x100>;
+ interrupts = <29>;
+ };
+
uart0: serial@01c28000 {
compatible = "snps,dw-apb-uart";
reg = <0x01c28000 0x400>;
--
1.8.4.2
^ permalink raw reply related
* [PATCH v3 4/5] ARM: dts: sun5i: Add rtp controller node
From: Hans de Goede @ 2013-12-31 16:20 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: linux-input-u79uwXL29TY76Z2rM5mHXA, LM Sensors, Maxime Ripard,
Corentin LABBE, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Hans de Goede
In-Reply-To: <1388506852-3548-1-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
arch/arm/boot/dts/sun5i-a10s.dtsi | 6 ++++++
arch/arm/boot/dts/sun5i-a13.dtsi | 6 ++++++
2 files changed, 12 insertions(+)
diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi
index 948eb10..34dd303 100644
--- a/arch/arm/boot/dts/sun5i-a10s.dtsi
+++ b/arch/arm/boot/dts/sun5i-a10s.dtsi
@@ -409,6 +409,12 @@
reg = <0x01c23800 0x10>;
};
+ rtp: rtp@01c25000 {
+ compatible = "allwinner,sun4i-ts";
+ reg = <0x01c25000 0x100>;
+ interrupts = <29>;
+ };
+
uart0: serial@01c28000 {
compatible = "snps,dw-apb-uart";
reg = <0x01c28000 0x400>;
diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi
index ca15672..264cfa4 100644
--- a/arch/arm/boot/dts/sun5i-a13.dtsi
+++ b/arch/arm/boot/dts/sun5i-a13.dtsi
@@ -367,6 +367,12 @@
reg = <0x01c23800 0x10>;
};
+ rtp: rtp@01c25000 {
+ compatible = "allwinner,sun4i-ts";
+ reg = <0x01c25000 0x100>;
+ interrupts = <29>;
+ };
+
uart1: serial@01c28400 {
compatible = "snps,dw-apb-uart";
reg = <0x01c28400 0x400>;
--
1.8.4.2
^ permalink raw reply related
* [PATCH v3 5/5] ARM: dts: sun7i: Add rtp controller node
From: Hans de Goede @ 2013-12-31 16:20 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: linux-input-u79uwXL29TY76Z2rM5mHXA, LM Sensors, Maxime Ripard,
Corentin LABBE, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Hans de Goede
In-Reply-To: <1388506852-3548-1-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
arch/arm/boot/dts/sun7i-a20.dtsi | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 3231789..68e825a 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -536,6 +536,12 @@
reg = <0x01c23800 0x200>;
};
+ rtp: rtp@01c25000 {
+ compatible = "allwinner,sun4i-ts";
+ reg = <0x01c25000 0x100>;
+ interrupts = <0 29 4>;
+ };
+
uart0: serial@01c28000 {
compatible = "snps,dw-apb-uart";
reg = <0x01c28000 0x400>;
--
1.8.4.2
^ permalink raw reply related
* Re: [PATCH V5] input synaptics-rmi4: Bug fixes to ATTN GPIO handling.
From: Dmitry Torokhov @ 2013-12-31 18:43 UTC (permalink / raw)
To: Christopher Heiny
Cc: Linux Input, Andrew Duggan, Vincent Huang, Vivian Ly,
Daniel Rosenberg, Jean Delvare, Joerie de Gram, Linus Walleij,
Benjamin Tissoires
In-Reply-To: <1388455525-30610-1-git-send-email-cheiny@synaptics.com>
On Mon, Dec 30, 2013 at 06:05:25PM -0800, Christopher Heiny wrote:
> This patch fixes some bugs in handling of the RMI4 attention line GPIO.
>
> 1) in enable_sensor(), eliminate the complicated check on ATTN and just
> call process_interrupt_requests(). This will have minimal overhead if ATTN
> is not asserted, and clears the state of the RMI4 device in any case.
>
> 2) Correctly free the GPIO in rmi_driver_remove().
>
> 3) in rmi_driver_probe()
> - declare the name of the attention gpio (GPIO_LABEL)
> - use gpio_request_one() to get the gpio and export it.
> - simplify (somewhat) conditional gpio acquisition logic and combine
> with interrupt setup
>
> 4) use gpio_is_valid() instead of comparing to 0.
>
> Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Applied, thank you.
--
Dmitry
^ permalink raw reply
* Re: [PATCH] input synaptics-rmi4 trivial: rmi_driver.c tidy-up
From: Dmitry Torokhov @ 2013-12-31 18:44 UTC (permalink / raw)
To: Christopher Heiny
Cc: Linux Input, Andrew Duggan, Vincent Huang, Vivian Ly,
Daniel Rosenberg, Jean Delvare, Joerie de Gram, Linus Walleij,
Benjamin Tissoires
In-Reply-To: <1388458486-31102-1-git-send-email-cheiny@synaptics.com>
On Mon, Dec 30, 2013 at 06:54:46PM -0800, Christopher Heiny wrote:
> This has three trivial changes:
>
> 1) multi-line commenting is changed to kernel coding standard
>
> 2) reset_and_reflash() function is renamed to rmi_initial_reset() and
> recommentedto reflect what it actually does.
>
> 3) #endif comment was updated to actuall match the opening #if CONFIG_PM_SLEEP
> condition.
>
> Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Applied, thank you.
--
Dmitry
^ permalink raw reply
* Re: [PATCH] input synaptics-rmi4 trivial: rmi_f01.c tidy-up
From: Dmitry Torokhov @ 2013-12-31 18:44 UTC (permalink / raw)
To: Christopher Heiny
Cc: Linux Input, Andrew Duggan, Vincent Huang, Vivian Ly,
Daniel Rosenberg, Jean Delvare, Joerie de Gram, Linus Walleij,
Benjamin Tissoires
In-Reply-To: <1388459456-7247-1-git-send-email-cheiny@synaptics.com>
On Mon, Dec 30, 2013 at 07:10:56PM -0800, Christopher Heiny wrote:
> This has two trivial changes:
>
> 1) make sure spacing around operators is correct.
>
> 2) capitalize Synaptics appropriately.
>
> Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Applied, thank you.
--
Dmitry
^ permalink raw reply
* Re: [PATCH 0/2] input: mt: Add helper function to send end events
From: Dmitry Torokhov @ 2013-12-31 18:50 UTC (permalink / raw)
To: Henrik Rydberg; +Cc: Felipe F. Tonello, linux-input, linux-kernel
In-Reply-To: <52C28DC7.9020003@euromail.se>
Hi Henrik,
On Tue, Dec 31, 2013 at 10:26:31AM +0100, Henrik Rydberg wrote:
> Hi Felipe,
>
> > Adds a helper function to send end touch events for active tracking ids.
> > And also implements it in egalax driver.
>
> What problematic scenario is this supposed to solve?
>
> The 'release on suspend' is only an approximation to the 'close
> laptop' scenario, it is certainly not correct in the coffee table
> case,
Why would it not be correct for coffee table? Do we expect the touches
to remain valid while the device is asleep?
> for instance. Instead of
> hardcoding this in the kernel, userland can easily detect long intervals
> directly using the event time.
But with slots it is not only problem with old events being sent out
later, it is that we have mix of old (pre-sleep) and new state.
>
> Unless there is already a reasonable mechanism in your user
> application which deals with cases like this but fails to account for
> your particular problem scenario, the patch will not be applied.
>
We do that for keys (release them when we transition to system sleep)
and I think it might be worthwhile to do the same for touch data.
Thanks.
--
Dmitry
^ permalink raw reply
* [git pull] Input updates for 3.13-rc6
From: Dmitry Torokhov @ 2013-12-31 19:06 UTC (permalink / raw)
To: Linus Torvalds; +Cc: linux-kernel, linux-input
[-- Attachment #1: Type: text/plain, Size: 928 bytes --]
Hi Linus,
Happy New Year!
Please pull from:
git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git for-linus
or
master.kernel.org:/pub/scm/linux/kernel/git/dtor/input.git for-linus
to receive updates for the input subsystem. You will get a fix for panic
in gpio-keys driver when set up with absolute events, a fixup to the new
zforce driver and a new keycode definition.
Changelog:
---------
Dmitry Torokhov (1):
Input: allocate absinfo data when setting ABS capability
Heiko Stübner (1):
Input: zforce - fix possible driver hang during suspend
Rafał Miłecki (1):
Input: define KEY_WWAN for Wireless WAN
Diffstat:
--------
drivers/input/input.c | 4 ++++
drivers/input/touchscreen/zforce_ts.c | 21 +++++++++++++++------
include/uapi/linux/input.h | 3 ++-
3 files changed, 21 insertions(+), 7 deletions(-)
--
Dmitry
[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox