141657514SCharles Mirabile // SPDX-License-Identifier: GPL-2.0-or-later
241657514SCharles Mirabile /*
341657514SCharles Mirabile * Raspberry Pi Sense HAT joystick driver
441657514SCharles Mirabile * http://raspberrypi.org
541657514SCharles Mirabile *
641657514SCharles Mirabile * Copyright (C) 2015 Raspberry Pi
741657514SCharles Mirabile * Copyright (C) 2021 Charles Mirabile, Mwesigwa Guma, Joel Savitz
841657514SCharles Mirabile *
941657514SCharles Mirabile * Original Author: Serge Schneider
1041657514SCharles Mirabile * Revised for upstream Linux by: Charles Mirabile, Mwesigwa Guma, Joel Savitz
1141657514SCharles Mirabile */
1241657514SCharles Mirabile
1341657514SCharles Mirabile #include <linux/module.h>
1441657514SCharles Mirabile #include <linux/input.h>
1541657514SCharles Mirabile #include <linux/i2c.h>
1641657514SCharles Mirabile #include <linux/interrupt.h>
1741657514SCharles Mirabile #include <linux/platform_device.h>
1841657514SCharles Mirabile #include <linux/regmap.h>
1941657514SCharles Mirabile #include <linux/property.h>
2041657514SCharles Mirabile
2141657514SCharles Mirabile #define JOYSTICK_SMB_REG 0xf2
2241657514SCharles Mirabile
2341657514SCharles Mirabile struct sensehat_joystick {
2441657514SCharles Mirabile struct platform_device *pdev;
2541657514SCharles Mirabile struct input_dev *keys_dev;
2641657514SCharles Mirabile unsigned long prev_states;
2741657514SCharles Mirabile struct regmap *regmap;
2841657514SCharles Mirabile };
2941657514SCharles Mirabile
3041657514SCharles Mirabile static const unsigned int keymap[] = {
3141657514SCharles Mirabile BTN_DPAD_DOWN, BTN_DPAD_RIGHT, BTN_DPAD_UP, BTN_SELECT, BTN_DPAD_LEFT,
3241657514SCharles Mirabile };
3341657514SCharles Mirabile
sensehat_joystick_report(int irq,void * cookie)3441657514SCharles Mirabile static irqreturn_t sensehat_joystick_report(int irq, void *cookie)
3541657514SCharles Mirabile {
3641657514SCharles Mirabile struct sensehat_joystick *sensehat_joystick = cookie;
3741657514SCharles Mirabile unsigned long curr_states, changes;
3841657514SCharles Mirabile unsigned int keys;
3941657514SCharles Mirabile int error;
4041657514SCharles Mirabile int i;
4141657514SCharles Mirabile
4241657514SCharles Mirabile error = regmap_read(sensehat_joystick->regmap, JOYSTICK_SMB_REG, &keys);
4341657514SCharles Mirabile if (error < 0) {
4441657514SCharles Mirabile dev_err(&sensehat_joystick->pdev->dev,
4541657514SCharles Mirabile "Failed to read joystick state: %d", error);
4641657514SCharles Mirabile return IRQ_NONE;
4741657514SCharles Mirabile }
4841657514SCharles Mirabile curr_states = keys;
4941657514SCharles Mirabile bitmap_xor(&changes, &curr_states, &sensehat_joystick->prev_states,
5041657514SCharles Mirabile ARRAY_SIZE(keymap));
5141657514SCharles Mirabile
5241657514SCharles Mirabile for_each_set_bit(i, &changes, ARRAY_SIZE(keymap))
5341657514SCharles Mirabile input_report_key(sensehat_joystick->keys_dev, keymap[i],
5441657514SCharles Mirabile curr_states & BIT(i));
5541657514SCharles Mirabile
5641657514SCharles Mirabile input_sync(sensehat_joystick->keys_dev);
5741657514SCharles Mirabile sensehat_joystick->prev_states = keys;
5841657514SCharles Mirabile return IRQ_HANDLED;
5941657514SCharles Mirabile }
6041657514SCharles Mirabile
sensehat_joystick_probe(struct platform_device * pdev)6141657514SCharles Mirabile static int sensehat_joystick_probe(struct platform_device *pdev)
6241657514SCharles Mirabile {
6341657514SCharles Mirabile struct sensehat_joystick *sensehat_joystick;
6441657514SCharles Mirabile int error, i, irq;
6541657514SCharles Mirabile
6641657514SCharles Mirabile sensehat_joystick = devm_kzalloc(&pdev->dev, sizeof(*sensehat_joystick),
6741657514SCharles Mirabile GFP_KERNEL);
6841657514SCharles Mirabile if (!sensehat_joystick)
6941657514SCharles Mirabile return -ENOMEM;
7041657514SCharles Mirabile
7141657514SCharles Mirabile sensehat_joystick->pdev = pdev;
7241657514SCharles Mirabile
7341657514SCharles Mirabile sensehat_joystick->regmap = dev_get_regmap(pdev->dev.parent, NULL);
7441657514SCharles Mirabile if (!sensehat_joystick->regmap) {
7541657514SCharles Mirabile dev_err(&pdev->dev, "unable to get sensehat regmap");
7641657514SCharles Mirabile return -ENODEV;
7741657514SCharles Mirabile }
7841657514SCharles Mirabile
7941657514SCharles Mirabile sensehat_joystick->keys_dev = devm_input_allocate_device(&pdev->dev);
8041657514SCharles Mirabile if (!sensehat_joystick->keys_dev) {
8141657514SCharles Mirabile dev_err(&pdev->dev, "Could not allocate input device");
8241657514SCharles Mirabile return -ENOMEM;
8341657514SCharles Mirabile }
8441657514SCharles Mirabile
8541657514SCharles Mirabile sensehat_joystick->keys_dev->name = "Raspberry Pi Sense HAT Joystick";
8641657514SCharles Mirabile sensehat_joystick->keys_dev->phys = "sensehat-joystick/input0";
8741657514SCharles Mirabile sensehat_joystick->keys_dev->id.bustype = BUS_I2C;
8841657514SCharles Mirabile
8941657514SCharles Mirabile __set_bit(EV_KEY, sensehat_joystick->keys_dev->evbit);
9041657514SCharles Mirabile __set_bit(EV_REP, sensehat_joystick->keys_dev->evbit);
9141657514SCharles Mirabile for (i = 0; i < ARRAY_SIZE(keymap); i++)
9241657514SCharles Mirabile __set_bit(keymap[i], sensehat_joystick->keys_dev->keybit);
9341657514SCharles Mirabile
9441657514SCharles Mirabile error = input_register_device(sensehat_joystick->keys_dev);
9541657514SCharles Mirabile if (error) {
9641657514SCharles Mirabile dev_err(&pdev->dev, "Could not register input device");
9741657514SCharles Mirabile return error;
9841657514SCharles Mirabile }
9941657514SCharles Mirabile
10041657514SCharles Mirabile irq = platform_get_irq(pdev, 0);
101*b2274ff2SYang Li if (irq < 0)
10241657514SCharles Mirabile return irq;
10341657514SCharles Mirabile
10441657514SCharles Mirabile error = devm_request_threaded_irq(&pdev->dev, irq,
10541657514SCharles Mirabile NULL, sensehat_joystick_report,
10641657514SCharles Mirabile IRQF_ONESHOT, "keys",
10741657514SCharles Mirabile sensehat_joystick);
10841657514SCharles Mirabile if (error) {
10941657514SCharles Mirabile dev_err(&pdev->dev, "IRQ request failed");
11041657514SCharles Mirabile return error;
11141657514SCharles Mirabile }
11241657514SCharles Mirabile
11341657514SCharles Mirabile return 0;
11441657514SCharles Mirabile }
11541657514SCharles Mirabile
11641657514SCharles Mirabile static const struct of_device_id sensehat_joystick_device_id[] = {
11741657514SCharles Mirabile { .compatible = "raspberrypi,sensehat-joystick" },
11841657514SCharles Mirabile {},
11941657514SCharles Mirabile };
12041657514SCharles Mirabile MODULE_DEVICE_TABLE(of, sensehat_joystick_device_id);
12141657514SCharles Mirabile
12241657514SCharles Mirabile static struct platform_driver sensehat_joystick_driver = {
12341657514SCharles Mirabile .probe = sensehat_joystick_probe,
12441657514SCharles Mirabile .driver = {
12541657514SCharles Mirabile .name = "sensehat-joystick",
12641657514SCharles Mirabile .of_match_table = sensehat_joystick_device_id,
12741657514SCharles Mirabile },
12841657514SCharles Mirabile };
12941657514SCharles Mirabile
13041657514SCharles Mirabile module_platform_driver(sensehat_joystick_driver);
13141657514SCharles Mirabile
13241657514SCharles Mirabile MODULE_DESCRIPTION("Raspberry Pi Sense HAT joystick driver");
13341657514SCharles Mirabile MODULE_AUTHOR("Charles Mirabile <cmirabil@redhat.com>");
13441657514SCharles Mirabile MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
13541657514SCharles Mirabile MODULE_LICENSE("GPL");
136