Skip to content

Commit 08866bc

Browse files
committed
Input: flipper-one-input: add support for SW buttons
Extend the flipper-one-input driver to support the MCU software driven buttons. While at that, refactor the code to reduce duplication. Signed-off-by: Alexey Charkov <alchark@flipper.net>
1 parent 60795c5 commit 08866bc

1 file changed

Lines changed: 144 additions & 118 deletions

File tree

drivers/input/misc/flipper-one-input.c

Lines changed: 144 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,20 @@
44
* Copyright (C) 2026 Flipper FZCO
55
*/
66

7-
#include <linux/input.h>
7+
#include <linux/array_size.h>
8+
#include <linux/bitops.h>
9+
#include <linux/bits.h>
10+
#include <linux/device.h>
11+
#include <linux/errno.h>
812
#include <linux/input-event-codes.h>
13+
#include <linux/input.h>
914
#include <linux/interrupt.h>
10-
#include <linux/irq.h>
11-
#include <linux/limits.h>
15+
#include <linux/mod_devicetable.h>
16+
#include <linux/module.h>
1217
#include <linux/platform_device.h>
18+
#include <linux/pm_wakeup.h>
1319
#include <linux/regmap.h>
14-
#include <linux/unaligned.h>
20+
#include <linux/types.h>
1521

1622
#include <linux/mfd/flipper-one-mcu.h>
1723

@@ -36,23 +42,12 @@
3642
#define FO_HS_BTN_C BIT(4)
3743
#define FO_HS_BTN_D BIT(5)
3844

39-
struct fo_input {
40-
struct input_dev *idev_btn;
41-
struct input_dev *idev_touch;
42-
struct input_dev *idev_headset;
43-
struct fomcu_device *fomcu;
44-
};
45-
46-
struct fo_irq {
47-
const char *name;
48-
irqreturn_t (*handler)(int, void *);
49-
};
45+
#define FO_SWBTN_POWER BIT(0)
5046

5147
static irqreturn_t fo_input_btn_handler(int irq, void *data)
5248
{
53-
struct fo_input *input = data;
54-
struct regmap *regmap = input->fomcu->regmap;
55-
struct input_dev *idev = input->idev_btn;
49+
struct input_dev *idev = data;
50+
struct regmap *regmap = input_get_drvdata(idev);
5651
struct device *parent = idev->dev.parent;
5752
unsigned int reg;
5853
int err;
@@ -81,13 +76,29 @@ static irqreturn_t fo_input_btn_handler(int irq, void *data)
8176
return IRQ_HANDLED;
8277
}
8378

79+
static void fo_input_setup_btn(struct input_dev *idev)
80+
{
81+
input_set_capability(idev, EV_KEY, KEY_ENTER); /* D-pad center */
82+
input_set_capability(idev, EV_KEY, KEY_UP); /* D-pad up */
83+
input_set_capability(idev, EV_KEY, KEY_DOWN); /* D-pad down */
84+
input_set_capability(idev, EV_KEY, KEY_LEFT); /* D-pad left */
85+
input_set_capability(idev, EV_KEY, KEY_RIGHT); /* D-pad right */
86+
input_set_capability(idev, EV_KEY, KEY_TAB); /* App switcher */
87+
input_set_capability(idev, EV_KEY, KEY_BACKSPACE); /* Back */
88+
input_set_capability(idev, EV_KEY, KEY_A); /* PTT */
89+
input_set_capability(idev, EV_KEY, KEY_Z); /* Escape */
90+
input_set_capability(idev, EV_KEY, KEY_X); /* View */
91+
input_set_capability(idev, EV_KEY, KEY_C); /* Power */
92+
input_set_capability(idev, EV_KEY, KEY_V); /* Edit */
93+
input_set_capability(idev, EV_KEY, KEY_B); /* Run */
94+
}
95+
8496
static irqreturn_t fo_input_touch_handler(int irq, void *data)
8597
{
86-
struct fo_input *input = data;
87-
struct regmap *regmap = input->fomcu->regmap;
88-
struct input_dev *idev = input->idev_touch;
98+
struct input_dev *idev = data;
99+
struct regmap *regmap = input_get_drvdata(idev);
89100
struct device *parent = idev->dev.parent;
90-
uint16_t buf[3];
101+
u16 buf[3];
91102
int err;
92103

93104
err = regmap_bulk_read(regmap, FOMCU_REG_INPUT_TOUCH_X, &buf, ARRAY_SIZE(buf));
@@ -106,11 +117,20 @@ static irqreturn_t fo_input_touch_handler(int irq, void *data)
106117
return IRQ_HANDLED;
107118
}
108119

120+
static void fo_input_setup_touch(struct input_dev *idev)
121+
{
122+
input_set_capability(idev, EV_KEY, BTN_TOUCH);
123+
input_set_capability(idev, EV_KEY, BTN_TOOL_FINGER);
124+
input_set_abs_params(idev, ABS_X, 0, 1024, 0, 0);
125+
input_set_abs_params(idev, ABS_Y, 0, 800, 0, 0);
126+
input_set_abs_params(idev, ABS_PRESSURE, 0, 12288, 0, 0);
127+
__set_bit(INPUT_PROP_POINTER, idev->propbit);
128+
}
129+
109130
static irqreturn_t fo_input_headset_handler(int irq, void *data)
110131
{
111-
struct fo_input *input = data;
112-
struct regmap *regmap = input->fomcu->regmap;
113-
struct input_dev *idev = input->idev_headset;
132+
struct input_dev *idev = data;
133+
struct regmap *regmap = input_get_drvdata(idev);
114134
struct device *parent = idev->dev.parent;
115135
unsigned int reg;
116136
int err;
@@ -132,118 +152,124 @@ static irqreturn_t fo_input_headset_handler(int irq, void *data)
132152
return IRQ_HANDLED;
133153
}
134154

135-
static const struct fo_irq fo_irqs[] = {
136-
{ .name = "flipper-one-input-btn", .handler = fo_input_btn_handler },
137-
{ .name = "flipper-one-input-touch", .handler = fo_input_touch_handler },
138-
{ .name = "flipper-one-input-headset", .handler = fo_input_headset_handler },
155+
static void fo_input_setup_headset(struct input_dev *idev)
156+
{
157+
input_set_capability(idev, EV_SW, SW_HEADPHONE_INSERT);
158+
input_set_capability(idev, EV_SW, SW_MICROPHONE_INSERT);
159+
input_set_capability(idev, EV_KEY, KEY_PLAYPAUSE);
160+
input_set_capability(idev, EV_KEY, KEY_VOLUMEUP);
161+
input_set_capability(idev, EV_KEY, KEY_VOLUMEDOWN);
162+
input_set_capability(idev, EV_KEY, KEY_VOICECOMMAND);
163+
}
164+
165+
static irqreturn_t fo_input_swbtn_handler(int irq, void *data)
166+
{
167+
struct input_dev *idev = data;
168+
struct regmap *regmap = input_get_drvdata(idev);
169+
struct device *parent = idev->dev.parent;
170+
unsigned int reg;
171+
int err;
172+
173+
err = regmap_read(regmap, FOMCU_REG_INPUT_SWBTNS, &reg);
174+
if (err) {
175+
dev_err(parent, "Failed to read SW button states: %d\n", err);
176+
return IRQ_NONE;
177+
}
178+
179+
input_report_key(idev, KEY_POWER, FO_SWBTN_POWER & reg);
180+
input_sync(idev);
181+
182+
return IRQ_HANDLED;
183+
}
184+
185+
static void fo_input_setup_swbtn(struct input_dev *idev)
186+
{
187+
input_set_capability(idev, EV_KEY, KEY_POWER);
188+
}
189+
190+
struct fo_input_config {
191+
const char *name;
192+
const char *phys;
193+
const char *irq_name;
194+
irqreturn_t (*handler)(int irq, void *data);
195+
void (*setup)(struct input_dev *idev);
196+
};
197+
198+
static const struct fo_input_config fo_input_configs[] = {
199+
{
200+
.name = "Flipper One Buttons",
201+
.phys = "flipper-one-input/input0",
202+
.irq_name = "flipper-one-input-btn",
203+
.handler = fo_input_btn_handler,
204+
.setup = fo_input_setup_btn,
205+
},
206+
{
207+
.name = "Flipper One Touchpad",
208+
.phys = "flipper-one-input/input1",
209+
.irq_name = "flipper-one-input-touch",
210+
.handler = fo_input_touch_handler,
211+
.setup = fo_input_setup_touch,
212+
},
213+
{
214+
.name = "Flipper One Headset",
215+
.phys = "flipper-one-input/input2",
216+
.irq_name = "flipper-one-input-headset",
217+
.handler = fo_input_headset_handler,
218+
.setup = fo_input_setup_headset,
219+
},
220+
{
221+
.name = "Flipper One Software Buttons",
222+
.phys = "flipper-one-input/input3",
223+
.irq_name = "flipper-one-input-swbtn",
224+
.handler = fo_input_swbtn_handler,
225+
.setup = fo_input_setup_swbtn,
226+
},
139227
};
140228

141229
static int fo_input_probe(struct platform_device *pdev)
142230
{
143231
struct fomcu_device *fomcu = dev_get_drvdata(pdev->dev.parent);
144232
struct device *dev = &pdev->dev;
145-
struct fo_input *input;
146-
struct input_dev *idev_btn, *idev_touch, *idev_headset;
147233
int irq, err, i;
148234

149-
input = devm_kzalloc(dev, sizeof(*input), GFP_KERNEL);
150-
if (!input)
151-
return -ENOMEM;
152-
153-
input->fomcu = fomcu;
154-
155-
idev_btn = devm_input_allocate_device(dev);
156-
if (!idev_btn) {
157-
dev_err(dev, "Failed to allocate buttons input device\n");
158-
return -ENOMEM;
159-
}
160-
input->idev_btn = idev_btn;
235+
err = devm_device_init_wakeup(dev);
236+
if (err)
237+
return dev_err_probe(dev, err, "Failed to init wakeup\n");
161238

162-
idev_btn->name = "Flipper One Buttons";
163-
idev_btn->phys = "flipper-one-input/input0";
164-
idev_btn->id.bustype = BUS_I2C;
239+
for (i = 0; i < ARRAY_SIZE(fo_input_configs); i++) {
240+
const struct fo_input_config *cfg = &fo_input_configs[i];
241+
struct input_dev *idev;
165242

166-
idev_touch = devm_input_allocate_device(dev);
167-
if (!idev_touch) {
168-
dev_err(dev, "Failed to allocate touch input device\n");
169-
return -ENOMEM;
170-
}
171-
input->idev_touch = idev_touch;
243+
idev = devm_input_allocate_device(dev);
244+
if (!idev)
245+
return dev_err_probe(dev, -ENOMEM,
246+
"Failed to allocate %s input device\n",
247+
cfg->name);
172248

173-
idev_touch->name = "Flipper One Touchpad";
174-
idev_touch->phys = "flipper-one-input/input1";
175-
idev_touch->id.bustype = BUS_I2C;
249+
idev->name = cfg->name;
250+
idev->phys = cfg->phys;
251+
idev->id.bustype = BUS_I2C;
252+
input_set_drvdata(idev, fomcu->regmap);
253+
cfg->setup(idev);
176254

177-
idev_headset = devm_input_allocate_device(dev);
178-
if (!idev_headset) {
179-
dev_err(dev, "Failed to allocate headset input device\n");
180-
return -ENOMEM;
181-
}
182-
input->idev_headset = idev_headset;
183-
184-
idev_headset->name = "Flipper One Headset";
185-
idev_headset->phys = "flipper-one-input/input2";
186-
idev_headset->id.bustype = BUS_I2C;
187-
188-
/* Buttons */
189-
input_set_capability(idev_btn, EV_KEY, KEY_ENTER); /* D-pad center */
190-
input_set_capability(idev_btn, EV_KEY, KEY_UP); /* D-pad up */
191-
input_set_capability(idev_btn, EV_KEY, KEY_DOWN); /* D-pad down */
192-
input_set_capability(idev_btn, EV_KEY, KEY_LEFT); /* D-pad left */
193-
input_set_capability(idev_btn, EV_KEY, KEY_RIGHT); /* D-pad right */
194-
input_set_capability(idev_btn, EV_KEY, KEY_TAB); /* App switcher */
195-
input_set_capability(idev_btn, EV_KEY, KEY_BACKSPACE); /* Back */
196-
input_set_capability(idev_btn, EV_KEY, KEY_A); /* PTT */
197-
input_set_capability(idev_btn, EV_KEY, KEY_Z); /* Escape */
198-
input_set_capability(idev_btn, EV_KEY, KEY_X); /* View */
199-
input_set_capability(idev_btn, EV_KEY, KEY_C); /* Power */
200-
input_set_capability(idev_btn, EV_KEY, KEY_V); /* Edit */
201-
input_set_capability(idev_btn, EV_KEY, KEY_B); /* Run */
202-
203-
/* Touchpad */
204-
input_set_capability(idev_touch, EV_KEY, BTN_TOUCH);
205-
input_set_capability(idev_touch, EV_KEY, BTN_TOOL_FINGER);
206-
input_set_abs_params(idev_touch, ABS_X, 0, 1024, 0, 0);
207-
input_set_abs_params(idev_touch, ABS_Y, 0, 800, 0, 0);
208-
input_set_abs_params(idev_touch, ABS_PRESSURE, 0, 12288, 0, 0);
209-
__set_bit(INPUT_PROP_POINTER, idev_touch->propbit);
210-
211-
/* Headset */
212-
input_set_capability(idev_headset, EV_SW, SW_HEADPHONE_INSERT);
213-
input_set_capability(idev_headset, EV_SW, SW_MICROPHONE_INSERT);
214-
input_set_capability(idev_headset, EV_KEY, KEY_PLAYPAUSE);
215-
input_set_capability(idev_headset, EV_KEY, KEY_VOLUMEUP);
216-
input_set_capability(idev_headset, EV_KEY, KEY_VOLUMEDOWN);
217-
input_set_capability(idev_headset, EV_KEY, KEY_VOICECOMMAND);
218-
219-
device_set_wakeup_capable(dev, true);
220-
device_wakeup_enable(dev);
221-
222-
for (i = 0; i < ARRAY_SIZE(fo_irqs); i++) {
223-
irq = platform_get_irq_byname(pdev, fo_irqs[i].name);
255+
irq = platform_get_irq_byname(pdev, cfg->irq_name);
224256
if (irq < 0)
225257
return dev_err_probe(dev, irq, "Failed to get IRQ %s\n",
226-
fo_irqs[i].name);
258+
cfg->irq_name);
227259

228-
err = devm_request_threaded_irq(dev, irq, NULL, fo_irqs[i].handler,
260+
err = devm_request_threaded_irq(dev, irq, NULL, cfg->handler,
229261
IRQF_ONESHOT | IRQF_NO_SUSPEND,
230-
fo_irqs[i].name, input);
262+
cfg->irq_name, idev);
231263
if (err)
232264
return dev_err_probe(dev, err, "Failed to request IRQ %s\n",
233-
fo_irqs[i].name);
234-
}
265+
cfg->irq_name);
235266

236-
err = input_register_device(idev_btn);
237-
if (err)
238-
return dev_err_probe(dev, err, "Failed to register buttons input device\n");
239-
240-
err = input_register_device(idev_touch);
241-
if (err)
242-
return dev_err_probe(dev, err, "Failed to register touch input device\n");
243-
244-
err = input_register_device(idev_headset);
245-
if (err)
246-
return dev_err_probe(dev, err, "Failed to register headset input device\n");
267+
err = input_register_device(idev);
268+
if (err)
269+
return dev_err_probe(dev, err,
270+
"Failed to register %s input device\n",
271+
cfg->name);
272+
}
247273

248274
return 0;
249275
}

0 commit comments

Comments
 (0)