Input: add Raspberry Pi Sense HAT joystick driver
authorCharles Mirabile <cmirabil@redhat.com>
Wed, 20 Apr 2022 21:00:20 +0000 (14:00 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Mon, 25 Apr 2022 01:25:02 +0000 (18:25 -0700)
This patch adds the driver for the Sense HAT joystick. It outputs BTN_DPAD
key events when moved in any of the four directions and the BTN_SELECT
event when depressed.

Co-developed-by: Daniel Bauman <dbauman@redhat.com>
Signed-off-by: Daniel Bauman <dbauman@redhat.com>
Co-developed-by: Mwesigwa Guma <mguma@redhat.com>
Signed-off-by: Mwesigwa Guma <mguma@redhat.com>
Co-developed-by: Joel Savitz <jsavitz@redhat.com>
Signed-off-by: Joel Savitz <jsavitz@redhat.com>
Signed-off-by: Charles Mirabile <cmirabil@redhat.com>
Link: https://lore.kernel.org/r/20220419205158.28088-3-cmirabil@redhat.com
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
drivers/input/joystick/Kconfig
drivers/input/joystick/Makefile
drivers/input/joystick/sensehat-joystick.c [new file with mode: 0644]

index 3b23078..505a032 100644 (file)
@@ -399,4 +399,15 @@ config JOYSTICK_N64
          Say Y here if you want enable support for the four
          built-in controller ports on the Nintendo 64 console.
 
+config JOYSTICK_SENSEHAT
+       tristate "Raspberry Pi Sense HAT joystick"
+       depends on INPUT && I2C
+       select MFD_SIMPLE_MFD_I2C
+       help
+         Say Y here if you want to enable the driver for the
+         the Raspberry Pi Sense HAT.
+
+         To compile this driver as a module, choose M here: the
+         module will be called sensehat_joystick.
+
 endif
index 5174b8a..3937535 100644 (file)
@@ -28,6 +28,7 @@ obj-$(CONFIG_JOYSTICK_N64)            += n64joy.o
 obj-$(CONFIG_JOYSTICK_PSXPAD_SPI)      += psxpad-spi.o
 obj-$(CONFIG_JOYSTICK_PXRC)            += pxrc.o
 obj-$(CONFIG_JOYSTICK_QWIIC)           += qwiic-joystick.o
+obj-$(CONFIG_JOYSTICK_SENSEHAT)        += sensehat-joystick.o
 obj-$(CONFIG_JOYSTICK_SIDEWINDER)      += sidewinder.o
 obj-$(CONFIG_JOYSTICK_SPACEBALL)       += spaceball.o
 obj-$(CONFIG_JOYSTICK_SPACEORB)                += spaceorb.o
diff --git a/drivers/input/joystick/sensehat-joystick.c b/drivers/input/joystick/sensehat-joystick.c
new file mode 100644 (file)
index 0000000..5ad1fe4
--- /dev/null
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Raspberry Pi Sense HAT joystick driver
+ * http://raspberrypi.org
+ *
+ * Copyright (C) 2015 Raspberry Pi
+ * Copyright (C) 2021 Charles Mirabile, Mwesigwa Guma, Joel Savitz
+ *
+ * Original Author: Serge Schneider
+ * Revised for upstream Linux by: Charles Mirabile, Mwesigwa Guma, Joel Savitz
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/property.h>
+
+#define JOYSTICK_SMB_REG 0xf2
+
+struct sensehat_joystick {
+       struct platform_device *pdev;
+       struct input_dev *keys_dev;
+       unsigned long prev_states;
+       struct regmap *regmap;
+};
+
+static const unsigned int keymap[] = {
+       BTN_DPAD_DOWN, BTN_DPAD_RIGHT, BTN_DPAD_UP, BTN_SELECT, BTN_DPAD_LEFT,
+};
+
+static irqreturn_t sensehat_joystick_report(int irq, void *cookie)
+{
+       struct sensehat_joystick *sensehat_joystick = cookie;
+       unsigned long curr_states, changes;
+       unsigned int keys;
+       int error;
+       int i;
+
+       error = regmap_read(sensehat_joystick->regmap, JOYSTICK_SMB_REG, &keys);
+       if (error < 0) {
+               dev_err(&sensehat_joystick->pdev->dev,
+                       "Failed to read joystick state: %d", error);
+               return IRQ_NONE;
+       }
+       curr_states = keys;
+       bitmap_xor(&changes, &curr_states, &sensehat_joystick->prev_states,
+                  ARRAY_SIZE(keymap));
+
+       for_each_set_bit(i, &changes, ARRAY_SIZE(keymap))
+               input_report_key(sensehat_joystick->keys_dev, keymap[i],
+                                curr_states & BIT(i));
+
+       input_sync(sensehat_joystick->keys_dev);
+       sensehat_joystick->prev_states = keys;
+       return IRQ_HANDLED;
+}
+
+static int sensehat_joystick_probe(struct platform_device *pdev)
+{
+       struct sensehat_joystick *sensehat_joystick;
+       int error, i, irq;
+
+       sensehat_joystick = devm_kzalloc(&pdev->dev, sizeof(*sensehat_joystick),
+                                        GFP_KERNEL);
+       if (!sensehat_joystick)
+               return -ENOMEM;
+
+       sensehat_joystick->pdev = pdev;
+
+       sensehat_joystick->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+       if (!sensehat_joystick->regmap) {
+               dev_err(&pdev->dev, "unable to get sensehat regmap");
+               return -ENODEV;
+       }
+
+       sensehat_joystick->keys_dev = devm_input_allocate_device(&pdev->dev);
+       if (!sensehat_joystick->keys_dev) {
+               dev_err(&pdev->dev, "Could not allocate input device");
+               return -ENOMEM;
+       }
+
+       sensehat_joystick->keys_dev->name = "Raspberry Pi Sense HAT Joystick";
+       sensehat_joystick->keys_dev->phys = "sensehat-joystick/input0";
+       sensehat_joystick->keys_dev->id.bustype = BUS_I2C;
+
+       __set_bit(EV_KEY, sensehat_joystick->keys_dev->evbit);
+       __set_bit(EV_REP, sensehat_joystick->keys_dev->evbit);
+       for (i = 0; i < ARRAY_SIZE(keymap); i++)
+               __set_bit(keymap[i], sensehat_joystick->keys_dev->keybit);
+
+       error = input_register_device(sensehat_joystick->keys_dev);
+       if (error) {
+               dev_err(&pdev->dev, "Could not register input device");
+               return error;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "Could not retrieve interrupt request");
+               return irq;
+       }
+
+       error = devm_request_threaded_irq(&pdev->dev, irq,
+                                         NULL, sensehat_joystick_report,
+                                         IRQF_ONESHOT, "keys",
+                                         sensehat_joystick);
+       if (error) {
+               dev_err(&pdev->dev, "IRQ request failed");
+               return error;
+       }
+
+       return 0;
+}
+
+static const struct of_device_id sensehat_joystick_device_id[] = {
+       { .compatible = "raspberrypi,sensehat-joystick" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, sensehat_joystick_device_id);
+
+static struct platform_driver sensehat_joystick_driver = {
+       .probe = sensehat_joystick_probe,
+       .driver = {
+               .name = "sensehat-joystick",
+               .of_match_table = sensehat_joystick_device_id,
+       },
+};
+
+module_platform_driver(sensehat_joystick_driver);
+
+MODULE_DESCRIPTION("Raspberry Pi Sense HAT joystick driver");
+MODULE_AUTHOR("Charles Mirabile <cmirabil@redhat.com>");
+MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
+MODULE_LICENSE("GPL");