xref: /openbmc/linux/drivers/input/joystick/sensehat-joystick.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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