12c2b364fSArtur Rojek // SPDX-License-Identifier: GPL-2.0
22c2b364fSArtur Rojek /*
32c2b364fSArtur Rojek * Input driver for joysticks connected over ADC.
42c2b364fSArtur Rojek * Copyright (c) 2019-2020 Artur Rojek <contact@artur-rojek.eu>
52c2b364fSArtur Rojek */
62c2b364fSArtur Rojek #include <linux/ctype.h>
72c2b364fSArtur Rojek #include <linux/input.h>
82c2b364fSArtur Rojek #include <linux/iio/iio.h>
92c2b364fSArtur Rojek #include <linux/iio/consumer.h>
102c2b364fSArtur Rojek #include <linux/module.h>
112c2b364fSArtur Rojek #include <linux/platform_device.h>
122c2b364fSArtur Rojek #include <linux/property.h>
132c2b364fSArtur Rojek
142c2b364fSArtur Rojek #include <asm/unaligned.h>
152c2b364fSArtur Rojek
162c2b364fSArtur Rojek struct adc_joystick_axis {
172c2b364fSArtur Rojek u32 code;
182c2b364fSArtur Rojek s32 range[2];
192c2b364fSArtur Rojek s32 fuzz;
202c2b364fSArtur Rojek s32 flat;
212c2b364fSArtur Rojek };
222c2b364fSArtur Rojek
232c2b364fSArtur Rojek struct adc_joystick {
242c2b364fSArtur Rojek struct input_dev *input;
252c2b364fSArtur Rojek struct iio_cb_buffer *buffer;
262c2b364fSArtur Rojek struct adc_joystick_axis *axes;
272c2b364fSArtur Rojek struct iio_channel *chans;
282c2b364fSArtur Rojek int num_chans;
29*24c06e00SChris Morgan bool polled;
302c2b364fSArtur Rojek };
312c2b364fSArtur Rojek
adc_joystick_poll(struct input_dev * input)32*24c06e00SChris Morgan static void adc_joystick_poll(struct input_dev *input)
33*24c06e00SChris Morgan {
34*24c06e00SChris Morgan struct adc_joystick *joy = input_get_drvdata(input);
35*24c06e00SChris Morgan int i, val, ret;
36*24c06e00SChris Morgan
37*24c06e00SChris Morgan for (i = 0; i < joy->num_chans; i++) {
38*24c06e00SChris Morgan ret = iio_read_channel_raw(&joy->chans[i], &val);
39*24c06e00SChris Morgan if (ret < 0)
40*24c06e00SChris Morgan return;
41*24c06e00SChris Morgan input_report_abs(input, joy->axes[i].code, val);
42*24c06e00SChris Morgan }
43*24c06e00SChris Morgan input_sync(input);
44*24c06e00SChris Morgan }
45*24c06e00SChris Morgan
adc_joystick_handle(const void * data,void * private)462c2b364fSArtur Rojek static int adc_joystick_handle(const void *data, void *private)
472c2b364fSArtur Rojek {
482c2b364fSArtur Rojek struct adc_joystick *joy = private;
492c2b364fSArtur Rojek enum iio_endian endianness;
502c2b364fSArtur Rojek int bytes, msb, val, idx, i;
512c2b364fSArtur Rojek const u16 *data_u16;
522c2b364fSArtur Rojek bool sign;
532c2b364fSArtur Rojek
542c2b364fSArtur Rojek bytes = joy->chans[0].channel->scan_type.storagebits >> 3;
552c2b364fSArtur Rojek
562c2b364fSArtur Rojek for (i = 0; i < joy->num_chans; ++i) {
572c2b364fSArtur Rojek idx = joy->chans[i].channel->scan_index;
582c2b364fSArtur Rojek endianness = joy->chans[i].channel->scan_type.endianness;
592c2b364fSArtur Rojek msb = joy->chans[i].channel->scan_type.realbits - 1;
602c2b364fSArtur Rojek sign = tolower(joy->chans[i].channel->scan_type.sign) == 's';
612c2b364fSArtur Rojek
622c2b364fSArtur Rojek switch (bytes) {
632c2b364fSArtur Rojek case 1:
642c2b364fSArtur Rojek val = ((const u8 *)data)[idx];
652c2b364fSArtur Rojek break;
662c2b364fSArtur Rojek case 2:
672c2b364fSArtur Rojek data_u16 = (const u16 *)data + idx;
682c2b364fSArtur Rojek
692c2b364fSArtur Rojek /*
702c2b364fSArtur Rojek * Data is aligned to the sample size by IIO core.
712c2b364fSArtur Rojek * Call `get_unaligned_xe16` to hide type casting.
722c2b364fSArtur Rojek */
732c2b364fSArtur Rojek if (endianness == IIO_BE)
742c2b364fSArtur Rojek val = get_unaligned_be16(data_u16);
752c2b364fSArtur Rojek else if (endianness == IIO_LE)
762c2b364fSArtur Rojek val = get_unaligned_le16(data_u16);
772c2b364fSArtur Rojek else /* IIO_CPU */
782c2b364fSArtur Rojek val = *data_u16;
792c2b364fSArtur Rojek break;
802c2b364fSArtur Rojek default:
812c2b364fSArtur Rojek return -EINVAL;
822c2b364fSArtur Rojek }
832c2b364fSArtur Rojek
842c2b364fSArtur Rojek val >>= joy->chans[i].channel->scan_type.shift;
852c2b364fSArtur Rojek if (sign)
862c2b364fSArtur Rojek val = sign_extend32(val, msb);
872c2b364fSArtur Rojek else
882c2b364fSArtur Rojek val &= GENMASK(msb, 0);
892c2b364fSArtur Rojek input_report_abs(joy->input, joy->axes[i].code, val);
902c2b364fSArtur Rojek }
912c2b364fSArtur Rojek
922c2b364fSArtur Rojek input_sync(joy->input);
932c2b364fSArtur Rojek
942c2b364fSArtur Rojek return 0;
952c2b364fSArtur Rojek }
962c2b364fSArtur Rojek
adc_joystick_open(struct input_dev * dev)972c2b364fSArtur Rojek static int adc_joystick_open(struct input_dev *dev)
982c2b364fSArtur Rojek {
992c2b364fSArtur Rojek struct adc_joystick *joy = input_get_drvdata(dev);
1002c2b364fSArtur Rojek struct device *devp = &dev->dev;
1012c2b364fSArtur Rojek int ret;
1022c2b364fSArtur Rojek
1032c2b364fSArtur Rojek ret = iio_channel_start_all_cb(joy->buffer);
1042c2b364fSArtur Rojek if (ret)
1052c2b364fSArtur Rojek dev_err(devp, "Unable to start callback buffer: %d\n", ret);
1062c2b364fSArtur Rojek
1072c2b364fSArtur Rojek return ret;
1082c2b364fSArtur Rojek }
1092c2b364fSArtur Rojek
adc_joystick_close(struct input_dev * dev)1102c2b364fSArtur Rojek static void adc_joystick_close(struct input_dev *dev)
1112c2b364fSArtur Rojek {
1122c2b364fSArtur Rojek struct adc_joystick *joy = input_get_drvdata(dev);
1132c2b364fSArtur Rojek
1142c2b364fSArtur Rojek iio_channel_stop_all_cb(joy->buffer);
1152c2b364fSArtur Rojek }
1162c2b364fSArtur Rojek
adc_joystick_cleanup(void * data)1172c2b364fSArtur Rojek static void adc_joystick_cleanup(void *data)
1182c2b364fSArtur Rojek {
1192c2b364fSArtur Rojek iio_channel_release_all_cb(data);
1202c2b364fSArtur Rojek }
1212c2b364fSArtur Rojek
adc_joystick_set_axes(struct device * dev,struct adc_joystick * joy)1222c2b364fSArtur Rojek static int adc_joystick_set_axes(struct device *dev, struct adc_joystick *joy)
1232c2b364fSArtur Rojek {
1242c2b364fSArtur Rojek struct adc_joystick_axis *axes;
1252c2b364fSArtur Rojek struct fwnode_handle *child;
1262c2b364fSArtur Rojek int num_axes, error, i;
1272c2b364fSArtur Rojek
1282c2b364fSArtur Rojek num_axes = device_get_child_node_count(dev);
1292c2b364fSArtur Rojek if (!num_axes) {
1302c2b364fSArtur Rojek dev_err(dev, "Unable to find child nodes\n");
1312c2b364fSArtur Rojek return -EINVAL;
1322c2b364fSArtur Rojek }
1332c2b364fSArtur Rojek
1342c2b364fSArtur Rojek if (num_axes != joy->num_chans) {
1352c2b364fSArtur Rojek dev_err(dev, "Got %d child nodes for %d channels\n",
1362c2b364fSArtur Rojek num_axes, joy->num_chans);
1372c2b364fSArtur Rojek return -EINVAL;
1382c2b364fSArtur Rojek }
1392c2b364fSArtur Rojek
1402c2b364fSArtur Rojek axes = devm_kmalloc_array(dev, num_axes, sizeof(*axes), GFP_KERNEL);
1412c2b364fSArtur Rojek if (!axes)
1422c2b364fSArtur Rojek return -ENOMEM;
1432c2b364fSArtur Rojek
1442c2b364fSArtur Rojek device_for_each_child_node(dev, child) {
1452c2b364fSArtur Rojek error = fwnode_property_read_u32(child, "reg", &i);
1462c2b364fSArtur Rojek if (error) {
1472c2b364fSArtur Rojek dev_err(dev, "reg invalid or missing\n");
1482c2b364fSArtur Rojek goto err_fwnode_put;
1492c2b364fSArtur Rojek }
1502c2b364fSArtur Rojek
1512c2b364fSArtur Rojek if (i >= num_axes) {
1522c2b364fSArtur Rojek error = -EINVAL;
1532c2b364fSArtur Rojek dev_err(dev, "No matching axis for reg %d\n", i);
1542c2b364fSArtur Rojek goto err_fwnode_put;
1552c2b364fSArtur Rojek }
1562c2b364fSArtur Rojek
1572c2b364fSArtur Rojek error = fwnode_property_read_u32(child, "linux,code",
1582c2b364fSArtur Rojek &axes[i].code);
1592c2b364fSArtur Rojek if (error) {
1602c2b364fSArtur Rojek dev_err(dev, "linux,code invalid or missing\n");
1612c2b364fSArtur Rojek goto err_fwnode_put;
1622c2b364fSArtur Rojek }
1632c2b364fSArtur Rojek
1642c2b364fSArtur Rojek error = fwnode_property_read_u32_array(child, "abs-range",
1652c2b364fSArtur Rojek axes[i].range, 2);
1662c2b364fSArtur Rojek if (error) {
1672c2b364fSArtur Rojek dev_err(dev, "abs-range invalid or missing\n");
1682c2b364fSArtur Rojek goto err_fwnode_put;
1692c2b364fSArtur Rojek }
1702c2b364fSArtur Rojek
1712c2b364fSArtur Rojek fwnode_property_read_u32(child, "abs-fuzz", &axes[i].fuzz);
1722c2b364fSArtur Rojek fwnode_property_read_u32(child, "abs-flat", &axes[i].flat);
1732c2b364fSArtur Rojek
1742c2b364fSArtur Rojek input_set_abs_params(joy->input, axes[i].code,
1752c2b364fSArtur Rojek axes[i].range[0], axes[i].range[1],
1762c2b364fSArtur Rojek axes[i].fuzz, axes[i].flat);
1772c2b364fSArtur Rojek input_set_capability(joy->input, EV_ABS, axes[i].code);
1782c2b364fSArtur Rojek }
1792c2b364fSArtur Rojek
1802c2b364fSArtur Rojek joy->axes = axes;
1812c2b364fSArtur Rojek
1822c2b364fSArtur Rojek return 0;
1832c2b364fSArtur Rojek
1842c2b364fSArtur Rojek err_fwnode_put:
1852c2b364fSArtur Rojek fwnode_handle_put(child);
1862c2b364fSArtur Rojek return error;
1872c2b364fSArtur Rojek }
1882c2b364fSArtur Rojek
adc_joystick_probe(struct platform_device * pdev)1892c2b364fSArtur Rojek static int adc_joystick_probe(struct platform_device *pdev)
1902c2b364fSArtur Rojek {
1912c2b364fSArtur Rojek struct device *dev = &pdev->dev;
1922c2b364fSArtur Rojek struct adc_joystick *joy;
1932c2b364fSArtur Rojek struct input_dev *input;
1942c2b364fSArtur Rojek int error;
1952c2b364fSArtur Rojek int bits;
1962c2b364fSArtur Rojek int i;
197*24c06e00SChris Morgan unsigned int poll_interval;
1982c2b364fSArtur Rojek
1992c2b364fSArtur Rojek joy = devm_kzalloc(dev, sizeof(*joy), GFP_KERNEL);
2002c2b364fSArtur Rojek if (!joy)
2012c2b364fSArtur Rojek return -ENOMEM;
2022c2b364fSArtur Rojek
2032c2b364fSArtur Rojek joy->chans = devm_iio_channel_get_all(dev);
2042c2b364fSArtur Rojek if (IS_ERR(joy->chans)) {
2052c2b364fSArtur Rojek error = PTR_ERR(joy->chans);
2062c2b364fSArtur Rojek if (error != -EPROBE_DEFER)
2072c2b364fSArtur Rojek dev_err(dev, "Unable to get IIO channels");
2082c2b364fSArtur Rojek return error;
2092c2b364fSArtur Rojek }
2102c2b364fSArtur Rojek
211*24c06e00SChris Morgan error = device_property_read_u32(dev, "poll-interval", &poll_interval);
212*24c06e00SChris Morgan if (error) {
213*24c06e00SChris Morgan /* -EINVAL means the property is absent. */
214*24c06e00SChris Morgan if (error != -EINVAL)
215*24c06e00SChris Morgan return error;
216*24c06e00SChris Morgan } else if (poll_interval == 0) {
217*24c06e00SChris Morgan dev_err(dev, "Unable to get poll-interval\n");
218*24c06e00SChris Morgan return -EINVAL;
219*24c06e00SChris Morgan } else {
220*24c06e00SChris Morgan joy->polled = true;
221*24c06e00SChris Morgan }
222*24c06e00SChris Morgan
223*24c06e00SChris Morgan /*
224*24c06e00SChris Morgan * Count how many channels we got. NULL terminated.
225*24c06e00SChris Morgan * Do not check the storage size if using polling.
226*24c06e00SChris Morgan */
2272c2b364fSArtur Rojek for (i = 0; joy->chans[i].indio_dev; i++) {
228*24c06e00SChris Morgan if (joy->polled)
229*24c06e00SChris Morgan continue;
2302c2b364fSArtur Rojek bits = joy->chans[i].channel->scan_type.storagebits;
2312c2b364fSArtur Rojek if (!bits || bits > 16) {
2322c2b364fSArtur Rojek dev_err(dev, "Unsupported channel storage size\n");
2332c2b364fSArtur Rojek return -EINVAL;
2342c2b364fSArtur Rojek }
2352c2b364fSArtur Rojek if (bits != joy->chans[0].channel->scan_type.storagebits) {
2362c2b364fSArtur Rojek dev_err(dev, "Channels must have equal storage size\n");
2372c2b364fSArtur Rojek return -EINVAL;
2382c2b364fSArtur Rojek }
2392c2b364fSArtur Rojek }
2402c2b364fSArtur Rojek joy->num_chans = i;
2412c2b364fSArtur Rojek
2422c2b364fSArtur Rojek input = devm_input_allocate_device(dev);
2432c2b364fSArtur Rojek if (!input) {
2442c2b364fSArtur Rojek dev_err(dev, "Unable to allocate input device\n");
2452c2b364fSArtur Rojek return -ENOMEM;
2462c2b364fSArtur Rojek }
2472c2b364fSArtur Rojek
2482c2b364fSArtur Rojek joy->input = input;
2492c2b364fSArtur Rojek input->name = pdev->name;
2502c2b364fSArtur Rojek input->id.bustype = BUS_HOST;
2512c2b364fSArtur Rojek
2522c2b364fSArtur Rojek error = adc_joystick_set_axes(dev, joy);
2532c2b364fSArtur Rojek if (error)
2542c2b364fSArtur Rojek return error;
2552c2b364fSArtur Rojek
256*24c06e00SChris Morgan if (joy->polled) {
257*24c06e00SChris Morgan input_setup_polling(input, adc_joystick_poll);
258*24c06e00SChris Morgan input_set_poll_interval(input, poll_interval);
259*24c06e00SChris Morgan } else {
260*24c06e00SChris Morgan input->open = adc_joystick_open;
261*24c06e00SChris Morgan input->close = adc_joystick_close;
262*24c06e00SChris Morgan
263*24c06e00SChris Morgan joy->buffer = iio_channel_get_all_cb(dev, adc_joystick_handle,
264*24c06e00SChris Morgan joy);
2652c2b364fSArtur Rojek if (IS_ERR(joy->buffer)) {
2662c2b364fSArtur Rojek dev_err(dev, "Unable to allocate callback buffer\n");
2672c2b364fSArtur Rojek return PTR_ERR(joy->buffer);
2682c2b364fSArtur Rojek }
2692c2b364fSArtur Rojek
270*24c06e00SChris Morgan error = devm_add_action_or_reset(dev, adc_joystick_cleanup,
271*24c06e00SChris Morgan joy->buffer);
2722c2b364fSArtur Rojek if (error) {
2732c2b364fSArtur Rojek dev_err(dev, "Unable to add action\n");
2742c2b364fSArtur Rojek return error;
2752c2b364fSArtur Rojek }
276*24c06e00SChris Morgan }
2772c2b364fSArtur Rojek
2787c744d00SDmitry Torokhov input_set_drvdata(input, joy);
2797c744d00SDmitry Torokhov
2807c744d00SDmitry Torokhov error = input_register_device(input);
2817c744d00SDmitry Torokhov if (error) {
2827c744d00SDmitry Torokhov dev_err(dev, "Unable to register input device\n");
2837c744d00SDmitry Torokhov return error;
2847c744d00SDmitry Torokhov }
2857c744d00SDmitry Torokhov
2862c2b364fSArtur Rojek return 0;
2872c2b364fSArtur Rojek }
2882c2b364fSArtur Rojek
2892c2b364fSArtur Rojek static const struct of_device_id adc_joystick_of_match[] = {
2902c2b364fSArtur Rojek { .compatible = "adc-joystick", },
2912c2b364fSArtur Rojek { }
2922c2b364fSArtur Rojek };
2932c2b364fSArtur Rojek MODULE_DEVICE_TABLE(of, adc_joystick_of_match);
2942c2b364fSArtur Rojek
2952c2b364fSArtur Rojek static struct platform_driver adc_joystick_driver = {
2962c2b364fSArtur Rojek .driver = {
2972c2b364fSArtur Rojek .name = "adc-joystick",
2982c2b364fSArtur Rojek .of_match_table = adc_joystick_of_match,
2992c2b364fSArtur Rojek },
3002c2b364fSArtur Rojek .probe = adc_joystick_probe,
3012c2b364fSArtur Rojek };
3022c2b364fSArtur Rojek module_platform_driver(adc_joystick_driver);
3032c2b364fSArtur Rojek
3042c2b364fSArtur Rojek MODULE_DESCRIPTION("Input driver for joysticks connected over ADC");
3052c2b364fSArtur Rojek MODULE_AUTHOR("Artur Rojek <contact@artur-rojek.eu>");
3062c2b364fSArtur Rojek MODULE_LICENSE("GPL");
307