aboutsummaryrefslogtreecommitdiff
path: root/drivers/input/joystick/adc-joystick.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/joystick/adc-joystick.c')
-rw-r--r--drivers/input/joystick/adc-joystick.c130
1 files changed, 79 insertions, 51 deletions
diff --git a/drivers/input/joystick/adc-joystick.c b/drivers/input/joystick/adc-joystick.c
index c0deff5d4282..5f46a7104b52 100644
--- a/drivers/input/joystick/adc-joystick.c
+++ b/drivers/input/joystick/adc-joystick.c
@@ -15,20 +15,26 @@
struct adc_joystick_axis {
u32 code;
- s32 range[2];
- s32 fuzz;
- s32 flat;
+ bool inverted;
};
struct adc_joystick {
struct input_dev *input;
struct iio_cb_buffer *buffer;
- struct adc_joystick_axis *axes;
struct iio_channel *chans;
- int num_chans;
- bool polled;
+ unsigned int num_chans;
+ struct adc_joystick_axis axes[] __counted_by(num_chans);
};
+static int adc_joystick_invert(struct input_dev *dev,
+ unsigned int axis, int val)
+{
+ int min = input_abs_get_min(dev, axis);
+ int max = input_abs_get_max(dev, axis);
+
+ return (max + min) - val;
+}
+
static void adc_joystick_poll(struct input_dev *input)
{
struct adc_joystick *joy = input_get_drvdata(input);
@@ -38,6 +44,8 @@ static void adc_joystick_poll(struct input_dev *input)
ret = iio_read_channel_raw(&joy->chans[i], &val);
if (ret < 0)
return;
+ if (joy->axes[i].inverted)
+ val = adc_joystick_invert(input, i, val);
input_report_abs(input, joy->axes[i].code, val);
}
input_sync(input);
@@ -86,6 +94,8 @@ static int adc_joystick_handle(const void *data, void *private)
val = sign_extend32(val, msb);
else
val &= GENMASK(msb, 0);
+ if (joy->axes[i].inverted)
+ val = adc_joystick_invert(joy->input, i, val);
input_report_abs(joy->input, joy->axes[i].code, val);
}
@@ -121,9 +131,11 @@ static void adc_joystick_cleanup(void *data)
static int adc_joystick_set_axes(struct device *dev, struct adc_joystick *joy)
{
- struct adc_joystick_axis *axes;
+ struct adc_joystick_axis *axes = joy->axes;
struct fwnode_handle *child;
- int num_axes, error, i;
+ s32 range[2], fuzz, flat;
+ unsigned int num_axes;
+ int error, i;
num_axes = device_get_child_node_count(dev);
if (!num_axes) {
@@ -137,10 +149,6 @@ 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);
- if (!axes)
- return -ENOMEM;
-
device_for_each_child_node(dev, child) {
error = fwnode_property_read_u32(child, "reg", &i);
if (error) {
@@ -162,23 +170,25 @@ static int adc_joystick_set_axes(struct device *dev, struct adc_joystick *joy)
}
error = fwnode_property_read_u32_array(child, "abs-range",
- axes[i].range, 2);
+ range, 2);
if (error) {
dev_err(dev, "abs-range invalid or missing\n");
goto err_fwnode_put;
}
- fwnode_property_read_u32(child, "abs-fuzz", &axes[i].fuzz);
- fwnode_property_read_u32(child, "abs-flat", &axes[i].flat);
+ if (range[0] > range[1]) {
+ dev_dbg(dev, "abs-axis %d inverted\n", i);
+ axes[i].inverted = true;
+ swap(range[0], range[1]);
+ }
+
+ fwnode_property_read_u32(child, "abs-fuzz", &fuzz);
+ fwnode_property_read_u32(child, "abs-flat", &flat);
input_set_abs_params(joy->input, axes[i].code,
- axes[i].range[0], axes[i].range[1],
- axes[i].fuzz, axes[i].flat);
- input_set_capability(joy->input, EV_ABS, axes[i].code);
+ range[0], range[1], fuzz, flat);
}
- joy->axes = axes;
-
return 0;
err_fwnode_put:
@@ -186,23 +196,50 @@ err_fwnode_put:
return error;
}
+
+static int adc_joystick_count_channels(struct device *dev,
+ const struct iio_channel *chans,
+ bool polled,
+ unsigned int *num_chans)
+{
+ int bits;
+ int i;
+
+ /*
+ * Count how many channels we got. NULL terminated.
+ * Do not check the storage size if using polling.
+ */
+ for (i = 0; chans[i].indio_dev; i++) {
+ if (polled)
+ continue;
+ bits = chans[i].channel->scan_type.storagebits;
+ if (!bits || bits > 16) {
+ dev_err(dev, "Unsupported channel storage size\n");
+ return -EINVAL;
+ }
+ if (bits != chans[0].channel->scan_type.storagebits) {
+ dev_err(dev, "Channels must have equal storage size\n");
+ return -EINVAL;
+ }
+ }
+
+ *num_chans = i;
+ return 0;
+}
+
static int adc_joystick_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct iio_channel *chans;
struct adc_joystick *joy;
struct input_dev *input;
+ unsigned int poll_interval = 0;
+ unsigned int num_chans;
int error;
- int bits;
- int i;
- unsigned int poll_interval;
-
- joy = devm_kzalloc(dev, sizeof(*joy), GFP_KERNEL);
- if (!joy)
- return -ENOMEM;
- joy->chans = devm_iio_channel_get_all(dev);
- if (IS_ERR(joy->chans)) {
- error = PTR_ERR(joy->chans);
+ chans = devm_iio_channel_get_all(dev);
+ error = PTR_ERR_OR_ZERO(chans);
+ if (error) {
if (error != -EPROBE_DEFER)
dev_err(dev, "Unable to get IIO channels");
return error;
@@ -216,28 +253,19 @@ static int adc_joystick_probe(struct platform_device *pdev)
} else if (poll_interval == 0) {
dev_err(dev, "Unable to get poll-interval\n");
return -EINVAL;
- } else {
- joy->polled = true;
}
- /*
- * Count how many channels we got. NULL terminated.
- * Do not check the storage size if using polling.
- */
- for (i = 0; joy->chans[i].indio_dev; i++) {
- if (joy->polled)
- continue;
- bits = joy->chans[i].channel->scan_type.storagebits;
- if (!bits || bits > 16) {
- dev_err(dev, "Unsupported channel storage size\n");
- return -EINVAL;
- }
- if (bits != joy->chans[0].channel->scan_type.storagebits) {
- dev_err(dev, "Channels must have equal storage size\n");
- return -EINVAL;
- }
- }
- joy->num_chans = i;
+ error = adc_joystick_count_channels(dev, chans, poll_interval != 0,
+ &num_chans);
+ if (error)
+ return error;
+
+ joy = devm_kzalloc(dev, struct_size(joy, axes, num_chans), GFP_KERNEL);
+ if (!joy)
+ return -ENOMEM;
+
+ joy->chans = chans;
+ joy->num_chans = num_chans;
input = devm_input_allocate_device(dev);
if (!input) {
@@ -253,7 +281,7 @@ static int adc_joystick_probe(struct platform_device *pdev)
if (error)
return error;
- if (joy->polled) {
+ if (poll_interval != 0) {
input_setup_polling(input, adc_joystick_poll);
input_set_poll_interval(input, poll_interval);
} else {