136 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			136 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0-or-later
 | |
| /*
 | |
|  * Raspberry Pi Sense HAT joystick driver
 | |
|  * http://raspberrypi.org
 | |
|  *
 | |
|  * Copyright (C) 2015 Raspberry Pi
 | |
|  * Copyright (C) 2021 Charles Mirabile, Mwesigwa Guma, Joel Savitz
 | |
|  *
 | |
|  * Original Author: Serge Schneider
 | |
|  * Revised for upstream Linux by: Charles Mirabile, Mwesigwa Guma, Joel Savitz
 | |
|  */
 | |
| 
 | |
| #include <linux/module.h>
 | |
| #include <linux/input.h>
 | |
| #include <linux/i2c.h>
 | |
| #include <linux/interrupt.h>
 | |
| #include <linux/platform_device.h>
 | |
| #include <linux/regmap.h>
 | |
| #include <linux/property.h>
 | |
| 
 | |
| #define JOYSTICK_SMB_REG 0xf2
 | |
| 
 | |
| struct sensehat_joystick {
 | |
| 	struct platform_device *pdev;
 | |
| 	struct input_dev *keys_dev;
 | |
| 	unsigned long prev_states;
 | |
| 	struct regmap *regmap;
 | |
| };
 | |
| 
 | |
| static const unsigned int keymap[] = {
 | |
| 	BTN_DPAD_DOWN, BTN_DPAD_RIGHT, BTN_DPAD_UP, BTN_SELECT, BTN_DPAD_LEFT,
 | |
| };
 | |
| 
 | |
| static irqreturn_t sensehat_joystick_report(int irq, void *cookie)
 | |
| {
 | |
| 	struct sensehat_joystick *sensehat_joystick = cookie;
 | |
| 	unsigned long curr_states, changes;
 | |
| 	unsigned int keys;
 | |
| 	int error;
 | |
| 	int i;
 | |
| 
 | |
| 	error = regmap_read(sensehat_joystick->regmap, JOYSTICK_SMB_REG, &keys);
 | |
| 	if (error < 0) {
 | |
| 		dev_err(&sensehat_joystick->pdev->dev,
 | |
| 			"Failed to read joystick state: %d", error);
 | |
| 		return IRQ_NONE;
 | |
| 	}
 | |
| 	curr_states = keys;
 | |
| 	bitmap_xor(&changes, &curr_states, &sensehat_joystick->prev_states,
 | |
| 		   ARRAY_SIZE(keymap));
 | |
| 
 | |
| 	for_each_set_bit(i, &changes, ARRAY_SIZE(keymap))
 | |
| 		input_report_key(sensehat_joystick->keys_dev, keymap[i],
 | |
| 				 curr_states & BIT(i));
 | |
| 
 | |
| 	input_sync(sensehat_joystick->keys_dev);
 | |
| 	sensehat_joystick->prev_states = keys;
 | |
| 	return IRQ_HANDLED;
 | |
| }
 | |
| 
 | |
| static int sensehat_joystick_probe(struct platform_device *pdev)
 | |
| {
 | |
| 	struct sensehat_joystick *sensehat_joystick;
 | |
| 	int error, i, irq;
 | |
| 
 | |
| 	sensehat_joystick = devm_kzalloc(&pdev->dev, sizeof(*sensehat_joystick),
 | |
| 					 GFP_KERNEL);
 | |
| 	if (!sensehat_joystick)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	sensehat_joystick->pdev = pdev;
 | |
| 
 | |
| 	sensehat_joystick->regmap = dev_get_regmap(pdev->dev.parent, NULL);
 | |
| 	if (!sensehat_joystick->regmap) {
 | |
| 		dev_err(&pdev->dev, "unable to get sensehat regmap");
 | |
| 		return -ENODEV;
 | |
| 	}
 | |
| 
 | |
| 	sensehat_joystick->keys_dev = devm_input_allocate_device(&pdev->dev);
 | |
| 	if (!sensehat_joystick->keys_dev) {
 | |
| 		dev_err(&pdev->dev, "Could not allocate input device");
 | |
| 		return -ENOMEM;
 | |
| 	}
 | |
| 
 | |
| 	sensehat_joystick->keys_dev->name = "Raspberry Pi Sense HAT Joystick";
 | |
| 	sensehat_joystick->keys_dev->phys = "sensehat-joystick/input0";
 | |
| 	sensehat_joystick->keys_dev->id.bustype = BUS_I2C;
 | |
| 
 | |
| 	__set_bit(EV_KEY, sensehat_joystick->keys_dev->evbit);
 | |
| 	__set_bit(EV_REP, sensehat_joystick->keys_dev->evbit);
 | |
| 	for (i = 0; i < ARRAY_SIZE(keymap); i++)
 | |
| 		__set_bit(keymap[i], sensehat_joystick->keys_dev->keybit);
 | |
| 
 | |
| 	error = input_register_device(sensehat_joystick->keys_dev);
 | |
| 	if (error) {
 | |
| 		dev_err(&pdev->dev, "Could not register input device");
 | |
| 		return error;
 | |
| 	}
 | |
| 
 | |
| 	irq = platform_get_irq(pdev, 0);
 | |
| 	if (irq < 0)
 | |
| 		return irq;
 | |
| 
 | |
| 	error = devm_request_threaded_irq(&pdev->dev, irq,
 | |
| 					  NULL, sensehat_joystick_report,
 | |
| 					  IRQF_ONESHOT, "keys",
 | |
| 					  sensehat_joystick);
 | |
| 	if (error) {
 | |
| 		dev_err(&pdev->dev, "IRQ request failed");
 | |
| 		return error;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static const struct of_device_id sensehat_joystick_device_id[] = {
 | |
| 	{ .compatible = "raspberrypi,sensehat-joystick" },
 | |
| 	{},
 | |
| };
 | |
| MODULE_DEVICE_TABLE(of, sensehat_joystick_device_id);
 | |
| 
 | |
| static struct platform_driver sensehat_joystick_driver = {
 | |
| 	.probe = sensehat_joystick_probe,
 | |
| 	.driver = {
 | |
| 		.name = "sensehat-joystick",
 | |
| 		.of_match_table = sensehat_joystick_device_id,
 | |
| 	},
 | |
| };
 | |
| 
 | |
| module_platform_driver(sensehat_joystick_driver);
 | |
| 
 | |
| MODULE_DESCRIPTION("Raspberry Pi Sense HAT joystick driver");
 | |
| MODULE_AUTHOR("Charles Mirabile <cmirabil@redhat.com>");
 | |
| MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
 | |
| MODULE_LICENSE("GPL");
 |