1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Raspberry Pi Sense HAT joystick driver 4 * http://raspberrypi.org 5 * 6 * Copyright (C) 2015 Raspberry Pi 7 * Copyright (C) 2021 Charles Mirabile, Mwesigwa Guma, Joel Savitz 8 * 9 * Original Author: Serge Schneider 10 * Revised for upstream Linux by: Charles Mirabile, Mwesigwa Guma, Joel Savitz 11 */ 12 13 #include <linux/module.h> 14 #include <linux/input.h> 15 #include <linux/i2c.h> 16 #include <linux/interrupt.h> 17 #include <linux/platform_device.h> 18 #include <linux/regmap.h> 19 #include <linux/property.h> 20 21 #define JOYSTICK_SMB_REG 0xf2 22 23 struct sensehat_joystick { 24 struct platform_device *pdev; 25 struct input_dev *keys_dev; 26 unsigned long prev_states; 27 struct regmap *regmap; 28 }; 29 30 static const unsigned int keymap[] = { 31 BTN_DPAD_DOWN, BTN_DPAD_RIGHT, BTN_DPAD_UP, BTN_SELECT, BTN_DPAD_LEFT, 32 }; 33 34 static irqreturn_t sensehat_joystick_report(int irq, void *cookie) 35 { 36 struct sensehat_joystick *sensehat_joystick = cookie; 37 unsigned long curr_states, changes; 38 unsigned int keys; 39 int error; 40 int i; 41 42 error = regmap_read(sensehat_joystick->regmap, JOYSTICK_SMB_REG, &keys); 43 if (error < 0) { 44 dev_err(&sensehat_joystick->pdev->dev, 45 "Failed to read joystick state: %d", error); 46 return IRQ_NONE; 47 } 48 curr_states = keys; 49 bitmap_xor(&changes, &curr_states, &sensehat_joystick->prev_states, 50 ARRAY_SIZE(keymap)); 51 52 for_each_set_bit(i, &changes, ARRAY_SIZE(keymap)) 53 input_report_key(sensehat_joystick->keys_dev, keymap[i], 54 curr_states & BIT(i)); 55 56 input_sync(sensehat_joystick->keys_dev); 57 sensehat_joystick->prev_states = keys; 58 return IRQ_HANDLED; 59 } 60 61 static int sensehat_joystick_probe(struct platform_device *pdev) 62 { 63 struct sensehat_joystick *sensehat_joystick; 64 int error, i, irq; 65 66 sensehat_joystick = devm_kzalloc(&pdev->dev, sizeof(*sensehat_joystick), 67 GFP_KERNEL); 68 if (!sensehat_joystick) 69 return -ENOMEM; 70 71 sensehat_joystick->pdev = pdev; 72 73 sensehat_joystick->regmap = dev_get_regmap(pdev->dev.parent, NULL); 74 if (!sensehat_joystick->regmap) { 75 dev_err(&pdev->dev, "unable to get sensehat regmap"); 76 return -ENODEV; 77 } 78 79 sensehat_joystick->keys_dev = devm_input_allocate_device(&pdev->dev); 80 if (!sensehat_joystick->keys_dev) { 81 dev_err(&pdev->dev, "Could not allocate input device"); 82 return -ENOMEM; 83 } 84 85 sensehat_joystick->keys_dev->name = "Raspberry Pi Sense HAT Joystick"; 86 sensehat_joystick->keys_dev->phys = "sensehat-joystick/input0"; 87 sensehat_joystick->keys_dev->id.bustype = BUS_I2C; 88 89 __set_bit(EV_KEY, sensehat_joystick->keys_dev->evbit); 90 __set_bit(EV_REP, sensehat_joystick->keys_dev->evbit); 91 for (i = 0; i < ARRAY_SIZE(keymap); i++) 92 __set_bit(keymap[i], sensehat_joystick->keys_dev->keybit); 93 94 error = input_register_device(sensehat_joystick->keys_dev); 95 if (error) { 96 dev_err(&pdev->dev, "Could not register input device"); 97 return error; 98 } 99 100 irq = platform_get_irq(pdev, 0); 101 if (irq < 0) { 102 dev_err(&pdev->dev, "Could not retrieve interrupt request"); 103 return irq; 104 } 105 106 error = devm_request_threaded_irq(&pdev->dev, irq, 107 NULL, sensehat_joystick_report, 108 IRQF_ONESHOT, "keys", 109 sensehat_joystick); 110 if (error) { 111 dev_err(&pdev->dev, "IRQ request failed"); 112 return error; 113 } 114 115 return 0; 116 } 117 118 static const struct of_device_id sensehat_joystick_device_id[] = { 119 { .compatible = "raspberrypi,sensehat-joystick" }, 120 {}, 121 }; 122 MODULE_DEVICE_TABLE(of, sensehat_joystick_device_id); 123 124 static struct platform_driver sensehat_joystick_driver = { 125 .probe = sensehat_joystick_probe, 126 .driver = { 127 .name = "sensehat-joystick", 128 .of_match_table = sensehat_joystick_device_id, 129 }, 130 }; 131 132 module_platform_driver(sensehat_joystick_driver); 133 134 MODULE_DESCRIPTION("Raspberry Pi Sense HAT joystick driver"); 135 MODULE_AUTHOR("Charles Mirabile <cmirabil@redhat.com>"); 136 MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>"); 137 MODULE_LICENSE("GPL"); 138