-
Notifications
You must be signed in to change notification settings - Fork 5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
mfd: Add Raspberry Pi Sense HAT core driver
mfd: Add rpi_sense_core of compatible string rpisense-fb: Set pseudo_pallete to prevent crash on fbcon takeover Signed-off-by: Serge Schneider <[email protected]> rpisense-fb: Add explicit fb_deferred_io_mmap hook As of commit [1], introduced in 5.18, fbdev drivers that use deferred IO and need mmap support must include an explicit fb_mmap pointer to the fb_deferred_io_mmap. Signed-off-by: Phil Elwell <[email protected]> [1] 5905585 ("fbdev: Put mmap for deferred I/O into drivers")
- Loading branch information
1 parent
cafd4cb
commit 0e65a76
Showing
12 changed files
with
760 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
/* | ||
* Raspberry Pi Sense HAT joystick driver | ||
* http://raspberrypi.org | ||
* | ||
* Copyright (C) 2015 Raspberry Pi | ||
* | ||
* Author: Serge Schneider | ||
* | ||
* This program is free software; you can redistribute it and/or modify it | ||
* under the terms of the GNU General Public License as published by the | ||
* Free Software Foundation; either version 2 of the License, or (at your | ||
* option) any later version. | ||
* | ||
*/ | ||
|
||
#include <linux/module.h> | ||
|
||
#include <linux/mfd/rpisense/joystick.h> | ||
#include <linux/mfd/rpisense/core.h> | ||
|
||
static struct rpisense *rpisense; | ||
static unsigned char keymap[5] = {KEY_DOWN, KEY_RIGHT, KEY_UP, KEY_ENTER, KEY_LEFT,}; | ||
|
||
static void keys_work_fn(struct work_struct *work) | ||
{ | ||
int i; | ||
static s32 prev_keys; | ||
struct rpisense_js *rpisense_js = &rpisense->joystick; | ||
s32 keys = rpisense_reg_read(rpisense, RPISENSE_KEYS); | ||
s32 changes = keys ^ prev_keys; | ||
|
||
prev_keys = keys; | ||
for (i = 0; i < 5; i++) { | ||
if (changes & 1) { | ||
input_report_key(rpisense_js->keys_dev, | ||
keymap[i], keys & 1); | ||
} | ||
changes >>= 1; | ||
keys >>= 1; | ||
} | ||
input_sync(rpisense_js->keys_dev); | ||
} | ||
|
||
static irqreturn_t keys_irq_handler(int irq, void *pdev) | ||
{ | ||
struct rpisense_js *rpisense_js = &rpisense->joystick; | ||
|
||
schedule_work(&rpisense_js->keys_work_s); | ||
return IRQ_HANDLED; | ||
} | ||
|
||
static int rpisense_js_probe(struct platform_device *pdev) | ||
{ | ||
int ret; | ||
int i; | ||
struct rpisense_js *rpisense_js; | ||
|
||
rpisense = rpisense_get_dev(); | ||
rpisense_js = &rpisense->joystick; | ||
|
||
INIT_WORK(&rpisense_js->keys_work_s, keys_work_fn); | ||
|
||
rpisense_js->keys_dev = input_allocate_device(); | ||
if (!rpisense_js->keys_dev) { | ||
dev_err(&pdev->dev, "Could not allocate input device.\n"); | ||
return -ENOMEM; | ||
} | ||
|
||
rpisense_js->keys_dev->evbit[0] = BIT_MASK(EV_KEY); | ||
for (i = 0; i < ARRAY_SIZE(keymap); i++) { | ||
set_bit(keymap[i], | ||
rpisense_js->keys_dev->keybit); | ||
} | ||
|
||
rpisense_js->keys_dev->name = "Raspberry Pi Sense HAT Joystick"; | ||
rpisense_js->keys_dev->phys = "rpi-sense-joy/input0"; | ||
rpisense_js->keys_dev->id.bustype = BUS_I2C; | ||
rpisense_js->keys_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); | ||
rpisense_js->keys_dev->keycode = keymap; | ||
rpisense_js->keys_dev->keycodesize = sizeof(unsigned char); | ||
rpisense_js->keys_dev->keycodemax = ARRAY_SIZE(keymap); | ||
|
||
ret = input_register_device(rpisense_js->keys_dev); | ||
if (ret) { | ||
dev_err(&pdev->dev, "Could not register input device.\n"); | ||
goto err_keys_alloc; | ||
} | ||
|
||
ret = gpiod_direction_input(rpisense_js->keys_desc); | ||
if (ret) { | ||
dev_err(&pdev->dev, "Could not set keys-int direction.\n"); | ||
goto err_keys_reg; | ||
} | ||
|
||
rpisense_js->keys_irq = gpiod_to_irq(rpisense_js->keys_desc); | ||
if (rpisense_js->keys_irq < 0) { | ||
dev_err(&pdev->dev, "Could not determine keys-int IRQ.\n"); | ||
ret = rpisense_js->keys_irq; | ||
goto err_keys_reg; | ||
} | ||
|
||
ret = devm_request_irq(&pdev->dev, rpisense_js->keys_irq, | ||
keys_irq_handler, IRQF_TRIGGER_RISING, | ||
"keys", &pdev->dev); | ||
if (ret) { | ||
dev_err(&pdev->dev, "IRQ request failed.\n"); | ||
goto err_keys_reg; | ||
} | ||
return 0; | ||
err_keys_reg: | ||
input_unregister_device(rpisense_js->keys_dev); | ||
err_keys_alloc: | ||
input_free_device(rpisense_js->keys_dev); | ||
return ret; | ||
} | ||
|
||
static int rpisense_js_remove(struct platform_device *pdev) | ||
{ | ||
struct rpisense_js *rpisense_js = &rpisense->joystick; | ||
|
||
input_unregister_device(rpisense_js->keys_dev); | ||
input_free_device(rpisense_js->keys_dev); | ||
return 0; | ||
} | ||
|
||
#ifdef CONFIG_OF | ||
static const struct of_device_id rpisense_js_id[] = { | ||
{ .compatible = "rpi,rpi-sense-js" }, | ||
{ }, | ||
}; | ||
MODULE_DEVICE_TABLE(of, rpisense_js_id); | ||
#endif | ||
|
||
static struct platform_device_id rpisense_js_device_id[] = { | ||
{ .name = "rpi-sense-js" }, | ||
{ }, | ||
}; | ||
MODULE_DEVICE_TABLE(platform, rpisense_js_device_id); | ||
|
||
static struct platform_driver rpisense_js_driver = { | ||
.probe = rpisense_js_probe, | ||
.remove = rpisense_js_remove, | ||
.driver = { | ||
.name = "rpi-sense-js", | ||
.owner = THIS_MODULE, | ||
}, | ||
}; | ||
|
||
module_platform_driver(rpisense_js_driver); | ||
|
||
MODULE_DESCRIPTION("Raspberry Pi Sense HAT joystick driver"); | ||
MODULE_AUTHOR("Serge Schneider <[email protected]>"); | ||
MODULE_LICENSE("GPL"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
/* | ||
* Raspberry Pi Sense HAT core driver | ||
* http://raspberrypi.org | ||
* | ||
* Copyright (C) 2015 Raspberry Pi | ||
* | ||
* Author: Serge Schneider | ||
* | ||
* This program is free software; you can redistribute it and/or modify it | ||
* under the terms of the GNU General Public License as published by the | ||
* Free Software Foundation; either version 2 of the License, or (at your | ||
* option) any later version. | ||
* | ||
* This driver is based on wm8350 implementation. | ||
*/ | ||
|
||
#include <linux/module.h> | ||
#include <linux/moduleparam.h> | ||
#include <linux/err.h> | ||
#include <linux/init.h> | ||
#include <linux/i2c.h> | ||
#include <linux/platform_device.h> | ||
#include <linux/mfd/rpisense/core.h> | ||
#include <linux/slab.h> | ||
|
||
static struct rpisense *rpisense; | ||
|
||
static void rpisense_client_dev_register(struct rpisense *rpisense, | ||
const char *name, | ||
struct platform_device **pdev) | ||
{ | ||
int ret; | ||
|
||
*pdev = platform_device_alloc(name, -1); | ||
if (*pdev == NULL) { | ||
dev_err(rpisense->dev, "Failed to allocate %s\n", name); | ||
return; | ||
} | ||
|
||
(*pdev)->dev.parent = rpisense->dev; | ||
platform_set_drvdata(*pdev, rpisense); | ||
ret = platform_device_add(*pdev); | ||
if (ret != 0) { | ||
dev_err(rpisense->dev, "Failed to register %s: %d\n", | ||
name, ret); | ||
platform_device_put(*pdev); | ||
*pdev = NULL; | ||
} | ||
} | ||
|
||
static int rpisense_probe(struct i2c_client *i2c) | ||
{ | ||
int ret; | ||
struct rpisense_js *rpisense_js; | ||
|
||
rpisense = devm_kzalloc(&i2c->dev, sizeof(struct rpisense), GFP_KERNEL); | ||
if (rpisense == NULL) | ||
return -ENOMEM; | ||
|
||
i2c_set_clientdata(i2c, rpisense); | ||
rpisense->dev = &i2c->dev; | ||
rpisense->i2c_client = i2c; | ||
|
||
ret = rpisense_reg_read(rpisense, RPISENSE_WAI); | ||
if (ret > 0) { | ||
if (ret != 's') | ||
return -EINVAL; | ||
} else { | ||
return ret; | ||
} | ||
ret = rpisense_reg_read(rpisense, RPISENSE_VER); | ||
if (ret < 0) | ||
return ret; | ||
|
||
dev_info(rpisense->dev, | ||
"Raspberry Pi Sense HAT firmware version %i\n", ret); | ||
|
||
rpisense_js = &rpisense->joystick; | ||
rpisense_js->keys_desc = devm_gpiod_get(&i2c->dev, | ||
"keys-int", GPIOD_IN); | ||
if (IS_ERR(rpisense_js->keys_desc)) { | ||
dev_warn(&i2c->dev, "Failed to get keys-int descriptor.\n"); | ||
rpisense_js->keys_desc = gpio_to_desc(23); | ||
if (rpisense_js->keys_desc == NULL) { | ||
dev_err(&i2c->dev, "GPIO23 fallback failed.\n"); | ||
return PTR_ERR(rpisense_js->keys_desc); | ||
} | ||
} | ||
rpisense_client_dev_register(rpisense, "rpi-sense-js", | ||
&(rpisense->joystick.pdev)); | ||
rpisense_client_dev_register(rpisense, "rpi-sense-fb", | ||
&(rpisense->framebuffer.pdev)); | ||
|
||
return 0; | ||
} | ||
|
||
static void rpisense_remove(struct i2c_client *i2c) | ||
{ | ||
struct rpisense *rpisense = i2c_get_clientdata(i2c); | ||
|
||
platform_device_unregister(rpisense->joystick.pdev); | ||
} | ||
|
||
struct rpisense *rpisense_get_dev(void) | ||
{ | ||
return rpisense; | ||
} | ||
EXPORT_SYMBOL_GPL(rpisense_get_dev); | ||
|
||
s32 rpisense_reg_read(struct rpisense *rpisense, int reg) | ||
{ | ||
int ret = i2c_smbus_read_byte_data(rpisense->i2c_client, reg); | ||
|
||
if (ret < 0) | ||
dev_err(rpisense->dev, "Read from reg %d failed\n", reg); | ||
/* Due to the BCM270x I2C clock stretching bug, some values | ||
* may have MSB set. Clear it to avoid incorrect values. | ||
* */ | ||
return ret & 0x7F; | ||
} | ||
EXPORT_SYMBOL_GPL(rpisense_reg_read); | ||
|
||
int rpisense_block_write(struct rpisense *rpisense, const char *buf, int count) | ||
{ | ||
int ret = i2c_master_send(rpisense->i2c_client, buf, count); | ||
|
||
if (ret < 0) | ||
dev_err(rpisense->dev, "Block write failed\n"); | ||
return ret; | ||
} | ||
EXPORT_SYMBOL_GPL(rpisense_block_write); | ||
|
||
static const struct i2c_device_id rpisense_i2c_id[] = { | ||
{ "rpi-sense", 0 }, | ||
{ } | ||
}; | ||
MODULE_DEVICE_TABLE(i2c, rpisense_i2c_id); | ||
|
||
#ifdef CONFIG_OF | ||
static const struct of_device_id rpisense_core_id[] = { | ||
{ .compatible = "rpi,rpi-sense" }, | ||
{ }, | ||
}; | ||
MODULE_DEVICE_TABLE(of, rpisense_core_id); | ||
#endif | ||
|
||
|
||
static struct i2c_driver rpisense_driver = { | ||
.driver = { | ||
.name = "rpi-sense", | ||
.owner = THIS_MODULE, | ||
}, | ||
.probe = rpisense_probe, | ||
.remove = rpisense_remove, | ||
.id_table = rpisense_i2c_id, | ||
}; | ||
|
||
module_i2c_driver(rpisense_driver); | ||
|
||
MODULE_DESCRIPTION("Raspberry Pi Sense HAT core driver"); | ||
MODULE_AUTHOR("Serge Schneider <[email protected]>"); | ||
MODULE_LICENSE("GPL"); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.