[RFC,2/2] Input: adc-joystick - add detachable devices support

Message ID 20221031190159.1341027-3-lis8215@gmail.com
State New
Headers
Series Input: adc-joystick: add detachable devices support |

Commit Message

Siarhei Volkau Oct. 31, 2022, 7:01 p.m. UTC
  For detachable or lock-able joysticks the ADC lanes might
be biased to GND or AVDD when the joystick is detached/locked.

One such kind of joystick is found in the Ritmix RZX-50 device.
The joystick is non-detachable, although ADC lane biased to power
supply when the "Hold" switch is activated.

To avoid reporting old/broken measurements valid-range is used.
When measured value is outside valid-range the driver reports
safe center position for corresponding axis.

Signed-off-by: Siarhei Volkau <lis8215@gmail.com>
---
 drivers/input/joystick/adc-joystick.c | 58 ++++++++++++++++++++++++++-
 1 file changed, 57 insertions(+), 1 deletion(-)
  

Patch

diff --git a/drivers/input/joystick/adc-joystick.c b/drivers/input/joystick/adc-joystick.c
index c0deff5d4..6a143dc38 100644
--- a/drivers/input/joystick/adc-joystick.c
+++ b/drivers/input/joystick/adc-joystick.c
@@ -18,6 +18,8 @@  struct adc_joystick_axis {
 	s32 range[2];
 	s32 fuzz;
 	s32 flat;
+	s32 valid[2];
+	s32 center;
 };
 
 struct adc_joystick {
@@ -29,6 +31,14 @@  struct adc_joystick {
 	bool polled;
 };
 
+static inline bool is_in_range(const s32 *range, int val)
+{
+	s32 min = range[0];
+	s32 max = range[1];
+
+	return (min == 0 && max == 0) || (val >= min && val <= max);
+}
+
 static void adc_joystick_poll(struct input_dev *input)
 {
 	struct adc_joystick *joy = input_get_drvdata(input);
@@ -38,6 +48,10 @@  static void adc_joystick_poll(struct input_dev *input)
 		ret = iio_read_channel_raw(&joy->chans[i], &val);
 		if (ret < 0)
 			return;
+
+		if (!is_in_range(joy->axes[i].valid, val))
+			val = joy->axes[i].center;
+
 		input_report_abs(input, joy->axes[i].code, val);
 	}
 	input_sync(input);
@@ -86,6 +100,10 @@  static int adc_joystick_handle(const void *data, void *private)
 			val = sign_extend32(val, msb);
 		else
 			val &= GENMASK(msb, 0);
+
+		if (!is_in_range(joy->axes[i].valid, val))
+			val = joy->axes[i].center;
+
 		input_report_abs(joy->input, joy->axes[i].code, val);
 	}
 
@@ -119,6 +137,21 @@  static void adc_joystick_cleanup(void *data)
 	iio_channel_release_all_cb(data);
 }
 
+static bool valid_range_covers_abs_range(struct adc_joystick_axis *axis)
+{
+	s32 abs_min, abs_max;
+
+	if (axis->range[0] > axis->range[1]) {
+		abs_min = axis->range[1];
+		abs_max = axis->range[0];
+	} else {
+		abs_min = axis->range[0];
+		abs_max = axis->range[1];
+	}
+
+	return axis->valid[0] <= abs_min && axis->valid[1] >= abs_max;
+}
+
 static int adc_joystick_set_axes(struct device *dev, struct adc_joystick *joy)
 {
 	struct adc_joystick_axis *axes;
@@ -137,7 +170,7 @@  static int adc_joystick_set_axes(struct device *dev, struct adc_joystick *joy)
 		return -EINVAL;
 	}
 
-	axes = devm_kmalloc_array(dev, num_axes, sizeof(*axes), GFP_KERNEL);
+	axes = devm_kcalloc(dev, num_axes, sizeof(*axes), GFP_KERNEL);
 	if (!axes)
 		return -ENOMEM;
 
@@ -167,10 +200,33 @@  static int adc_joystick_set_axes(struct device *dev, struct adc_joystick *joy)
 			dev_err(dev, "abs-range invalid or missing\n");
 			goto err_fwnode_put;
 		}
+		axes[i].center = (axes[i].range[0] + axes[i].range[1]) / 2;
 
 		fwnode_property_read_u32(child, "abs-fuzz", &axes[i].fuzz);
 		fwnode_property_read_u32(child, "abs-flat", &axes[i].flat);
 
+		if (fwnode_property_present(child, "valid-range")) {
+			error = fwnode_property_read_u32_array(child,
+						       "valid-range",
+						       axes[i].valid, 2);
+			if (error) {
+				dev_err(dev, "valid-range invalid\n");
+				goto err_fwnode_put;
+			}
+
+			if (axes[i].valid[0] > axes[i].valid[1]) {
+				dev_err(dev,
+					"valid-range invalid (min > max)\n");
+				goto err_fwnode_put;
+			}
+
+			if (!valid_range_covers_abs_range(&axes[i])) {
+				dev_err(dev,
+					"valid-range must cover abs-range\n");
+				goto err_fwnode_put;
+			}
+		}
+
 		input_set_abs_params(joy->input, axes[i].code,
 				     axes[i].range[0], axes[i].range[1],
 				     axes[i].fuzz, axes[i].flat);