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(map: sensehat_joystick->regmap, JOYSTICK_SMB_REG, val: &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(dst: &changes, src1: &curr_states, src2: &sensehat_joystick->prev_states, |
50 | ARRAY_SIZE(keymap)); |
51 | |
52 | for_each_set_bit(i, &changes, ARRAY_SIZE(keymap)) |
53 | input_report_key(dev: sensehat_joystick->keys_dev, code: keymap[i], |
54 | value: curr_states & BIT(i)); |
55 | |
56 | input_sync(dev: 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(dev: &pdev->dev, size: 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(dev: 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 | return irq; |
103 | |
104 | error = devm_request_threaded_irq(dev: &pdev->dev, irq, |
105 | NULL, thread_fn: sensehat_joystick_report, |
106 | IRQF_ONESHOT, devname: "keys" , |
107 | dev_id: 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" ); |
136 | |