All of lore.kernel.org
 help / color / mirror / Atom feed
From: Joel Savitz <jsavitz@redhat.com>
To: linux-rpi-kernel@lists.infradead.org
Cc: Joel Savitz <joelsavitz@gmail.com>,
	Nicolas Saenz Julienne <nsaenzjulienne@suse.de>,
	bcm-kernel-feedback-list@broadcom.com,
	linux-arm-kernel@lists.infradead.org,
	fedora-rpi@googlegroups.com, serge@raspberrypi.org
Subject: [RFC PATCH] drivers: Raspbery Pi Sense Hat
Date: Wed,  7 Apr 2021 20:03:53 -0400	[thread overview]
Message-ID: <20210408000353.3571132-1-jsavitz@redhat.com> (raw)

From: Joel Savitz <joelsavitz@gmail.com>

This is a forward port of the Rapsberry Pi Sense Hat driver from the
downstream Raspberry Pi kernel tree.

I have slightly consolidated things and done some quick testing of this
driver on Fedora (which comes with the device tree blobs), and I want to
do a bit more testing before submiting the driver for inclusion in
mainline Linux.

I would like to get some feedback from the community on what may need to
be improved, fixed, or changed as early as possible, so this is a request
for comments.

Signed-off-by: Joel Savitz <joelsavitz@gmail.com>
---
 drivers/input/joystick/Kconfig       |   8 +
 drivers/input/joystick/Makefile      |   1 +
 drivers/input/joystick/rpisense-js.c | 147 ++++++++++++++
 drivers/mfd/Kconfig                  |   8 +
 drivers/mfd/Makefile                 |   1 +
 drivers/mfd/rpisense-core.c          | 162 +++++++++++++++
 drivers/video/fbdev/Kconfig          |  13 ++
 drivers/video/fbdev/Makefile         |   1 +
 drivers/video/fbdev/rpisense-fb.c    | 291 +++++++++++++++++++++++++++
 include/linux/mfd/rpisense.h         |  66 ++++++
 10 files changed, 698 insertions(+)
 create mode 100644 drivers/input/joystick/rpisense-js.c
 create mode 100644 drivers/mfd/rpisense-core.c
 create mode 100644 drivers/video/fbdev/rpisense-fb.c
 create mode 100644 include/linux/mfd/rpisense.h

diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
index 5e38899058c1..1c0025c22bf4 100644
--- a/drivers/input/joystick/Kconfig
+++ b/drivers/input/joystick/Kconfig
@@ -389,4 +389,12 @@ config JOYSTICK_N64
 	  Say Y here if you want enable support for the four
 	  built-in controller ports on the Nintendo 64 console.
 
+config JOYSTICK_RPISENSE
+	tristate "Raspberry Pi Sense HAT joystick"
+	depends on GPIOLIB && INPUT
+	select MFD_RPISENSE_CORE
+
+	help
+	  This is the joystick driver for the Raspberry Pi Sense HAT
+
 endif
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
index 31d720c9e493..b227e98cd179 100644
--- a/drivers/input/joystick/Makefile
+++ b/drivers/input/joystick/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_JOYSTICK_MAPLE)		+= maplecontrol.o
 obj-$(CONFIG_JOYSTICK_N64)		+= n64joy.o
 obj-$(CONFIG_JOYSTICK_PSXPAD_SPI)	+= psxpad-spi.o
 obj-$(CONFIG_JOYSTICK_PXRC)		+= pxrc.o
+obj-$(CONFIG_JOYSTICK_RPISENSE)         += rpisense-js.o
 obj-$(CONFIG_JOYSTICK_SIDEWINDER)	+= sidewinder.o
 obj-$(CONFIG_JOYSTICK_SPACEBALL)	+= spaceball.o
 obj-$(CONFIG_JOYSTICK_SPACEORB)		+= spaceorb.o
diff --git a/drivers/input/joystick/rpisense-js.c b/drivers/input/joystick/rpisense-js.c
new file mode 100644
index 000000000000..41d4bd1fed46
--- /dev/null
+++ b/drivers/input/joystick/rpisense-js.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Raspberry Pi Sense HAT joystick driver
+ * http://raspberrypi.org
+ *
+ * Copyright (C) 2015 Raspberry Pi
+ *
+ * Author: Serge Schneider
+ * Adapted for mainline Linux by: Joel Savitz
+ */
+
+#include <linux/module.h>
+#include <linux/mfd/rpisense.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 = "raspberrypi,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 <serge@raspberrypi.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index b74efa469e90..8c309e389359 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -11,6 +11,14 @@ config MFD_CORE
 	select IRQ_DOMAIN
 	default n
 
+config MFD_RPISENSE_CORE
+	tristate "Raspberry Pi Sense HAT core functions"
+	depends on I2C
+	select MFD_CORE
+	help
+	  This is the core driver for the Raspberry Pi Sense HAT. This provides
+	  the necessary functions to communicate with the hardware.
+
 config MFD_CS5535
 	tristate "AMD CS5535 and CS5536 southbridge core functions"
 	select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 834f5463af28..7140a8fc36ee 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -263,6 +263,7 @@ obj-$(CONFIG_MFD_ROHM_BD71828)	+= rohm-bd71828.o
 obj-$(CONFIG_MFD_ROHM_BD718XX)	+= rohm-bd718x7.o
 obj-$(CONFIG_MFD_STMFX) 	+= stmfx.o
 obj-$(CONFIG_MFD_KHADAS_MCU) 	+= khadas-mcu.o
+obj-$(CONFIG_MFD_RPISENSE_CORE) += rpisense-core.o
 obj-$(CONFIG_MFD_ACER_A500_EC)	+= acer-ec-a500.o
 
 obj-$(CONFIG_SGI_MFD_IOC3)	+= ioc3.o
diff --git a/drivers/mfd/rpisense-core.c b/drivers/mfd/rpisense-core.c
new file mode 100644
index 000000000000..aa2c394c5e67
--- /dev/null
+++ b/drivers/mfd/rpisense-core.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Raspberry Pi Sense HAT core driver
+ * http://raspberrypi.org
+ *
+ * Copyright (C) 2015 Raspberry Pi
+ *
+ * Author: Serge Schneider
+ * Adapted for mainline Linux by: Joel Savitz
+ *
+ *  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/slab.h>
+
+#include <linux/mfd/rpisense.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,
+			       const struct i2c_device_id *id)
+{
+	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 int rpisense_remove(struct i2c_client *i2c)
+{
+	struct rpisense *rpisense = i2c_get_clientdata(i2c);
+
+	platform_device_unregister(rpisense->joystick.pdev);
+	return 0;
+}
+
+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 = "raspberrypi,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 <serge@raspberrypi.org>");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index 4f02db65dede..2238e36e71e4 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -2236,6 +2236,19 @@ config FB_SM712
 	  called sm712fb. If you want to compile it as a module, say M
 	  here and read <file:Documentation/kbuild/modules.rst>.
 
+config FB_RPISENSE
+        tristate "Raspberry Pi Sense HAT framebuffer"
+        depends on FB
+        select MFD_RPISENSE_CORE
+        select FB_SYS_FOPS
+        select FB_SYS_FILLRECT
+        select FB_SYS_COPYAREA
+        select FB_SYS_IMAGEBLIT
+        select FB_DEFERRED_IO
+
+        help
+          This is the framebuffer driver for the Raspberry Pi Sense HAT
+
 source "drivers/video/fbdev/omap/Kconfig"
 source "drivers/video/fbdev/omap2/Kconfig"
 source "drivers/video/fbdev/mmp/Kconfig"
diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile
index 477b9624b703..5b17203b7e43 100644
--- a/drivers/video/fbdev/Makefile
+++ b/drivers/video/fbdev/Makefile
@@ -129,6 +129,7 @@ obj-$(CONFIG_FB_MX3)		  += mx3fb.o
 obj-$(CONFIG_FB_DA8XX)		  += da8xx-fb.o
 obj-$(CONFIG_FB_SSD1307)	  += ssd1307fb.o
 obj-$(CONFIG_FB_SIMPLE)           += simplefb.o
+obj-$(CONFIG_FB_RPISENSE)         += rpisense-fb.o
 
 # the test framebuffer is last
 obj-$(CONFIG_FB_VIRTUAL)          += vfb.o
diff --git a/drivers/video/fbdev/rpisense-fb.c b/drivers/video/fbdev/rpisense-fb.c
new file mode 100644
index 000000000000..a90a5ec51fd7
--- /dev/null
+++ b/drivers/video/fbdev/rpisense-fb.c
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Raspberry Pi Sense HAT framebuffer driver
+ * http://raspberrypi.org
+ *
+ * Copyright (C) 2015 Raspberry Pi
+ *
+ * Author: Serge Schneider
+ * Adapted for mainline Linux by: Joel Savitz
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+
+#include <linux/mfd/rpisense.h>
+
+static bool lowlight;
+module_param(lowlight, bool, 0);
+MODULE_PARM_DESC(lowlight, "Reduce LED matrix brightness to one third");
+
+static struct rpisense *rpisense;
+
+struct rpisense_fb_param {
+	char __iomem *vmem;
+	u8 *vmem_work;
+	u32 vmemsize;
+	u8 *gamma;
+};
+
+static u8 gamma_default[32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+			       0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07,
+			       0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0E, 0x0F, 0x11,
+			       0x12, 0x14, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F,};
+
+static u8 gamma_low[32] = {0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+			   0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02,
+			   0x03, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06,
+			   0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x0A, 0x0A,};
+
+static u8 gamma_user[32];
+
+static u32 pseudo_palette[16];
+
+static struct rpisense_fb_param rpisense_fb_param = {
+	.vmem = NULL,
+	.vmemsize = 128,
+	.gamma = gamma_default,
+};
+
+static struct fb_deferred_io rpisense_fb_defio;
+
+static struct fb_fix_screeninfo rpisense_fb_fix = {
+	.id =		"RPi-Sense FB",
+	.type =		FB_TYPE_PACKED_PIXELS,
+	.visual =	FB_VISUAL_TRUECOLOR,
+	.xpanstep =	0,
+	.ypanstep =	0,
+	.ywrapstep =	0,
+	.accel =	FB_ACCEL_NONE,
+	.line_length =	16,
+};
+
+static struct fb_var_screeninfo rpisense_fb_var = {
+	.xres		= 8,
+	.yres		= 8,
+	.xres_virtual	= 8,
+	.yres_virtual	= 8,
+	.bits_per_pixel = 16,
+	.red		= {11, 5, 0},
+	.green		= {5, 6, 0},
+	.blue		= {0, 5, 0},
+};
+
+static ssize_t rpisense_fb_write(struct fb_info *info,
+				 const char __user *buf, size_t count,
+				 loff_t *ppos)
+{
+	ssize_t res = fb_sys_write(info, buf, count, ppos);
+
+	schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
+	return res;
+}
+
+static void rpisense_fb_fillrect(struct fb_info *info,
+				 const struct fb_fillrect *rect)
+{
+	sys_fillrect(info, rect);
+	schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
+}
+
+static void rpisense_fb_copyarea(struct fb_info *info,
+				 const struct fb_copyarea *area)
+{
+	sys_copyarea(info, area);
+	schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
+}
+
+static void rpisense_fb_imageblit(struct fb_info *info,
+				  const struct fb_image *image)
+{
+	sys_imageblit(info, image);
+	schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
+}
+
+static void rpisense_fb_deferred_io(struct fb_info *info,
+				struct list_head *pagelist)
+{
+	int i;
+	int j;
+	u8 *vmem_work = rpisense_fb_param.vmem_work;
+	u16 *mem = (u16 *)rpisense_fb_param.vmem;
+	u8 *gamma = rpisense_fb_param.gamma;
+
+	vmem_work[0] = 0;
+	for (j = 0; j < 8; j++) {
+		for (i = 0; i < 8; i++) {
+			vmem_work[(j * 24) + i + 1] =
+				gamma[(mem[(j * 8) + i] >> 11) & 0x1F];
+			vmem_work[(j * 24) + (i + 8) + 1] =
+				gamma[(mem[(j * 8) + i] >> 6) & 0x1F];
+			vmem_work[(j * 24) + (i + 16) + 1] =
+				gamma[(mem[(j * 8) + i]) & 0x1F];
+		}
+	}
+	rpisense_block_write(rpisense, vmem_work, 193);
+}
+
+static struct fb_deferred_io rpisense_fb_defio = {
+	.delay		= HZ/100,
+	.deferred_io	= rpisense_fb_deferred_io,
+};
+
+static int rpisense_fb_ioctl(struct fb_info *info, unsigned int cmd,
+			     unsigned long arg)
+{
+	switch (cmd) {
+	case SENSEFB_FBIOGET_GAMMA:
+		if (copy_to_user((void __user *) arg, rpisense_fb_param.gamma,
+				 sizeof(u8[32])))
+			return -EFAULT;
+		return 0;
+	case SENSEFB_FBIOSET_GAMMA:
+		if (copy_from_user(gamma_user, (void __user *)arg,
+				   sizeof(u8[32])))
+			return -EFAULT;
+		rpisense_fb_param.gamma = gamma_user;
+		schedule_delayed_work(&info->deferred_work,
+				      rpisense_fb_defio.delay);
+		return 0;
+	case SENSEFB_FBIORESET_GAMMA:
+		switch (arg) {
+		case 0:
+			rpisense_fb_param.gamma = gamma_default;
+			break;
+		case 1:
+			rpisense_fb_param.gamma = gamma_low;
+			break;
+		case 2:
+			rpisense_fb_param.gamma = gamma_user;
+			break;
+		default:
+			return -EINVAL;
+		}
+		schedule_delayed_work(&info->deferred_work,
+				      rpisense_fb_defio.delay);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static struct fb_ops rpisense_fb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_read	= fb_sys_read,
+	.fb_write	= rpisense_fb_write,
+	.fb_fillrect	= rpisense_fb_fillrect,
+	.fb_copyarea	= rpisense_fb_copyarea,
+	.fb_imageblit	= rpisense_fb_imageblit,
+	.fb_ioctl	= rpisense_fb_ioctl,
+};
+
+static int rpisense_fb_probe(struct platform_device *pdev)
+{
+	struct fb_info *info;
+	int ret = -ENOMEM;
+	struct rpisense_fb *rpisense_fb;
+
+	rpisense = rpisense_get_dev();
+	rpisense_fb = &rpisense->framebuffer;
+
+	rpisense_fb_param.vmem = vzalloc(rpisense_fb_param.vmemsize);
+	if (!rpisense_fb_param.vmem)
+		return ret;
+
+	rpisense_fb_param.vmem_work = devm_kmalloc(&pdev->dev, 193, GFP_KERNEL);
+	if (!rpisense_fb_param.vmem_work)
+		goto err_malloc;
+
+	info = framebuffer_alloc(0, &pdev->dev);
+	if (!info) {
+		dev_err(&pdev->dev, "Could not allocate framebuffer.\n");
+		goto err_malloc;
+	}
+	rpisense_fb->info = info;
+
+	rpisense_fb_fix.smem_start = (unsigned long)rpisense_fb_param.vmem;
+	rpisense_fb_fix.smem_len = rpisense_fb_param.vmemsize;
+
+	info->fbops = &rpisense_fb_ops;
+	info->fix = rpisense_fb_fix;
+	info->var = rpisense_fb_var;
+	info->fbdefio = &rpisense_fb_defio;
+	info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
+	info->screen_base = rpisense_fb_param.vmem;
+	info->screen_size = rpisense_fb_param.vmemsize;
+	info->pseudo_palette = pseudo_palette;
+
+	if (lowlight)
+		rpisense_fb_param.gamma = gamma_low;
+
+	fb_deferred_io_init(info);
+
+	ret = register_framebuffer(info);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Could not register framebuffer.\n");
+		goto err_fballoc;
+	}
+
+	fb_info(info, "%s frame buffer device\n", info->fix.id);
+	schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
+	return 0;
+err_fballoc:
+	framebuffer_release(info);
+err_malloc:
+	vfree(rpisense_fb_param.vmem);
+	return ret;
+}
+
+static int rpisense_fb_remove(struct platform_device *pdev)
+{
+	struct rpisense_fb *rpisense_fb = &rpisense->framebuffer;
+	struct fb_info *info = rpisense_fb->info;
+
+	if (info) {
+		unregister_framebuffer(info);
+		fb_deferred_io_cleanup(info);
+		framebuffer_release(info);
+		vfree(rpisense_fb_param.vmem);
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rpisense_fb_id[] = {
+	{ .compatible = "raspberrypi,rpi-sense-fb" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, rpisense_fb_id);
+#endif
+
+static struct platform_device_id rpisense_fb_device_id[] = {
+	{ .name = "rpi-sense-fb" },
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, rpisense_fb_device_id);
+
+static struct platform_driver rpisense_fb_driver = {
+	.probe = rpisense_fb_probe,
+	.remove = rpisense_fb_remove,
+	.driver = {
+		.name = "rpi-sense-fb",
+		.owner = THIS_MODULE,
+	},
+};
+
+module_platform_driver(rpisense_fb_driver);
+
+MODULE_DESCRIPTION("Raspberry Pi Sense HAT framebuffer driver");
+MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
+MODULE_LICENSE("GPL");
+
diff --git a/include/linux/mfd/rpisense.h b/include/linux/mfd/rpisense.h
new file mode 100644
index 000000000000..0718b6f0eddb
--- /dev/null
+++ b/include/linux/mfd/rpisense.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Raspberry Pi Sense HAT core driver
+ * http://raspberrypi.org
+ *
+ * Copyright (C) 2015 Raspberry Pi
+ *
+ * Author: Serge Schneider
+ * Adapted for mainline Linux by: Joel Savitz
+ */
+
+#ifndef __LINUX_MFD_RPISENSE_CORE_H_
+#define __LINUX_MFD_RPISENSE_CORE_H_
+
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+
+/*
+ * Register values.
+ */
+#define RPISENSE_FB			0x00
+#define RPISENSE_WAI			0xF0
+#define RPISENSE_VER			0xF1
+#define RPISENSE_KEYS			0xF2
+#define RPISENSE_EE_WP			0xF3
+
+#define RPISENSE_ID			's'
+
+#define SENSEFB_FBIO_IOC_MAGIC 0xF1
+
+#define SENSEFB_FBIOGET_GAMMA _IO(SENSEFB_FBIO_IOC_MAGIC, 0)
+#define SENSEFB_FBIOSET_GAMMA _IO(SENSEFB_FBIO_IOC_MAGIC, 1)
+#define SENSEFB_FBIORESET_GAMMA _IO(SENSEFB_FBIO_IOC_MAGIC, 2)
+
+struct rpisense_fb {
+	struct platform_device *pdev;
+	struct fb_info *info;
+};
+
+
+struct rpisense_js {
+	struct platform_device *pdev;
+	struct input_dev *keys_dev;
+	struct gpio_desc *keys_desc;
+	struct work_struct keys_work_s;
+	int keys_irq;
+};
+
+
+struct rpisense {
+	struct device *dev;
+	struct i2c_client *i2c_client;
+
+	/* Client devices */
+	struct rpisense_js joystick;
+	struct rpisense_fb framebuffer;
+};
+
+struct rpisense *rpisense_get_dev(void);
+s32 rpisense_reg_read(struct rpisense *rpisense, int reg);
+int rpisense_reg_write(struct rpisense *rpisense, int reg, u16 val);
+int rpisense_block_write(struct rpisense *rpisense, const char *buf, int count);
+
+#endif
-- 
2.27.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

             reply	other threads:[~2021-04-08  0:08 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-04-08  0:03 Joel Savitz [this message]
2021-04-08  9:21 ` [RFC PATCH] drivers: Raspbery Pi Sense Hat Peter Robinson
2021-04-08 16:16 ` Nicolas Saenz Julienne
2021-04-08 19:29 ` Stefan Wahren

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210408000353.3571132-1-jsavitz@redhat.com \
    --to=jsavitz@redhat.com \
    --cc=bcm-kernel-feedback-list@broadcom.com \
    --cc=fedora-rpi@googlegroups.com \
    --cc=joelsavitz@gmail.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-rpi-kernel@lists.infradead.org \
    --cc=nsaenzjulienne@suse.de \
    --cc=serge@raspberrypi.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.