Commit | Line | Data |
---|---|---|
41657514 CM |
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); | |
b2274ff2 | 101 | if (irq < 0) |
41657514 | 102 | return irq; |
41657514 CM |
103 | |
104 | error = devm_request_threaded_irq(&pdev->dev, irq, | |
105 | NULL, sensehat_joystick_report, | |
106 | IRQF_ONESHOT, "keys", | |
107 | sensehat_joystick); | |
108 | if (error) { | |
109 | dev_err(&pdev->dev, "IRQ request failed"); | |
110 | return error; | |
111 | } | |
112 | ||
113 | return 0; | |
114 | } | |
115 | ||
116 | static const struct of_device_id sensehat_joystick_device_id[] = { | |
117 | { .compatible = "raspberrypi,sensehat-joystick" }, | |
118 | {}, | |
119 | }; | |
120 | MODULE_DEVICE_TABLE(of, sensehat_joystick_device_id); | |
121 | ||
122 | static struct platform_driver sensehat_joystick_driver = { | |
123 | .probe = sensehat_joystick_probe, | |
124 | .driver = { | |
125 | .name = "sensehat-joystick", | |
126 | .of_match_table = sensehat_joystick_device_id, | |
127 | }, | |
128 | }; | |
129 | ||
130 | module_platform_driver(sensehat_joystick_driver); | |
131 | ||
132 | MODULE_DESCRIPTION("Raspberry Pi Sense HAT joystick driver"); | |
133 | MODULE_AUTHOR("Charles Mirabile <cmirabil@redhat.com>"); | |
134 | MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>"); | |
135 | MODULE_LICENSE("GPL"); |