LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [RFC PATCH 0/3] Raspberry Pi Sense HAT driver
@ 2021-08-07  0:27 Joel Savitz
  2021-08-07  0:27 ` [RFC PATCH 1/3] drivers/mfd: rpisense: Raspberry Pi senseHAT core driver Joel Savitz
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: Joel Savitz @ 2021-08-07  0:27 UTC (permalink / raw)
  To: linux-kernel
  Cc: Joel Savitz, Lee Jones, Stefan Wahren, Nicolas Saenz Julienne,
	linux-rpi-kernel, fedora-rpi, Charles Mirabile, Mwesigwa Guma

This patch series adds a set of drivers for operating the Sense HAT
peripheral device. This board is an add on for the Raspberry Pi that is
designed to connect using the GPIO connector via I2C.

It features:
	- a joystick
	- an 8x8 RGB LED matrix display
	- a whole bunch of environmental sensors with their own drivers
	(those are already in upstream Linux)

This is a refactor of the work of Serge Schneider, the author of a
version of this driver that is currently in the Raspberry Pi downstream
kernel. We modified his code to make it suitable for upstream Linux.

A couple of tests are available for this driver in this repo:
https://github.com/underground-software/sensehat/tree/master/tests
	- color_test displays various solid colors on the LED panel
	- framebuffer_test diplays a single lit cell that is
	  controllable via the arrow keys or the joystick

Though the driver is split into three modules, they are interdependent
and meaningless in isolation. As such, we have grouped them together.

This is a request for comments. Please let us know if you
have any suggestions, complements, or harsh criticism. ;)

Known issue:
- We do not know how to integrate the device tree overlay into mainline
  Linux. Fortunately, most Linux distrubtions designed for the Raspberry
  Pi already include the approriate device tree overlay as a part of
  their importation of all overlays from the downstream Raspberry Pi
  kernel. Suggestions are welcome and appreciated.

For more information about the Sense HAT, visit:
https://www.raspberrypi.org/products/sense-hat/

Signed-off-by: Charles Mirabile <cmirabil@redhat.com>
Signed-off-by: Mwesigwa Guma <mguma@redhat.com>
Signed-off-by: Joel Savitz <jsavitz@redhat.com>

Joel Savitz (3):
  drivers/mfd: rpisense: Raspberry Pi senseHAT core driver
  drivers/mfd: rpisense: Raspberry Pi senseHAT joystick driver
  drivers/mfd: rpisense: Raspberry Pi senseHAT 8x8 LED matrix display
    driver

 drivers/mfd/Kconfig            |  10 ++
 drivers/mfd/Makefile           |   3 +
 drivers/mfd/rpisense-core.c    | 170 +++++++++++++++++++++++
 drivers/mfd/rpisense-display.c | 242 +++++++++++++++++++++++++++++++++
 drivers/mfd/rpisense-js.c      | 132 ++++++++++++++++++
 include/linux/mfd/rpisense.h   |  55 ++++++++
 6 files changed, 612 insertions(+)
 create mode 100644 drivers/mfd/rpisense-core.c
 create mode 100644 drivers/mfd/rpisense-display.c
 create mode 100644 drivers/mfd/rpisense-js.c
 create mode 100644 include/linux/mfd/rpisense.h

-- 
2.27.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH 1/3] drivers/mfd: rpisense: Raspberry Pi senseHAT core driver
  2021-08-07  0:27 [RFC PATCH 0/3] Raspberry Pi Sense HAT driver Joel Savitz
@ 2021-08-07  0:27 ` Joel Savitz
  2021-08-07  0:32   ` Randy Dunlap
  2021-08-07  9:53   ` Stefan Wahren
  2021-08-07  0:27 ` [RFC PATCH 2/3] drivers/mfd: rpisense: Raspberry Pi senseHAT joystick driver Joel Savitz
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 9+ messages in thread
From: Joel Savitz @ 2021-08-07  0:27 UTC (permalink / raw)
  To: linux-kernel
  Cc: Joel Savitz, Lee Jones, Stefan Wahren, Nicolas Saenz Julienne,
	linux-rpi-kernel, fedora-rpi, Charles Mirabile, Mwesigwa Guma

This patch adds the core driver file, containing methods to communicate
with the board over I2C. We also add the header file shared by all
three drivers, containing common data and definitions. In addition, we
add a config option to toggle compilation of the driver.

Signed-off-by: Charles Mirabile <cmirabil@redhat.com>
Signed-off-by: Mwesigwa Guma <mguma@redhat.com>
Signed-off-by: Joel Savitz <jsavitz@redhat.com>
---
 drivers/mfd/Kconfig          |  10 +++
 drivers/mfd/Makefile         |   1 +
 drivers/mfd/rpisense-core.c  | 170 +++++++++++++++++++++++++++++++++++
 include/linux/mfd/rpisense.h |  55 ++++++++++++
 4 files changed, 236 insertions(+)
 create mode 100644 drivers/mfd/rpisense-core.c
 create mode 100644 include/linux/mfd/rpisense.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 6a3fd2d75f96..614de080dee6 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -11,6 +11,16 @@ config MFD_CORE
 	select IRQ_DOMAIN
 	default n
 
+config MFD_RPISENSE
+	tristate "Raspberry Pi Sense HAT driver"
+	depends on I2C && GPIOLIB
+	select MFD_CORE
+	help
+	  This is the driver for the Raspberry Pi Sense HAT. This provides
+	  the necessary functions to communicate with the hardware as well
+	  as a joystick and display interface. Linux communicates with the
+	  hardwire using the GPIO pins via the I2C protocol.
+
 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 8116c19d5fd4..76f9a9221241 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -263,6 +263,7 @@ obj-$(CONFIG_MFD_ROHM_BD718XX)	+= rohm-bd718x7.o
 obj-$(CONFIG_MFD_ROHM_BD957XMUF)	+= rohm-bd9576.o
 obj-$(CONFIG_MFD_STMFX) 	+= stmfx.o
 obj-$(CONFIG_MFD_KHADAS_MCU) 	+= khadas-mcu.o
+obj-$(CONFIG_MFD_RPISENSE) 	+= rpisense-core.o
 obj-$(CONFIG_MFD_ACER_A500_EC)	+= acer-ec-a500.o
 obj-$(CONFIG_MFD_QCOM_PM8008)	+= qcom-pm8008.o
 
diff --git a/drivers/mfd/rpisense-core.c b/drivers/mfd/rpisense-core.c
new file mode 100644
index 000000000000..69e3051a4be0
--- /dev/null
+++ b/drivers/mfd/rpisense-core.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Raspberry Pi Sense HAT core 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
+ *
+ * This driver is based on wm8350 implementation and was refactored to use the
+ * misc device subsystem rather than the deprecated framebuffer subsystem.
+ */
+
+#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/slab.h>
+#include <linux/mfd/rpisense.h>
+
+#define RPISENSE_DISPLAY		0x00
+#define RPISENSE_WAI			0xF0
+#define RPISENSE_VER			0xF1
+#define RPISENSE_KEYS			0xF2
+#define RPISENSE_EE_WP			0xF3
+
+#define RPISENSE_ID			's'
+
+static struct platform_device *
+rpisense_client_dev_register(struct rpisense *rpisense, const char *name);
+
+static int rpisense_probe(struct i2c_client *i2c,
+			       const struct i2c_device_id *id)
+{
+	int ret;
+
+	struct rpisense *rpisense = devm_kzalloc(&i2c->dev, sizeof(*rpisense), GFP_KERNEL);
+
+	if (rpisense == NULL)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, rpisense);
+	rpisense->dev = &i2c->dev;
+	rpisense->i2c_client = i2c;
+
+
+	ret = i2c_smbus_read_byte_data(rpisense->i2c_client, RPISENSE_WAI);
+	if (ret < 0)
+		return ret;
+
+	if (ret != RPISENSE_ID)
+		return -EINVAL;
+
+	ret = i2c_smbus_read_byte_data(rpisense->i2c_client, RPISENSE_VER);
+	if (ret < 0)
+		return ret;
+
+	dev_info(rpisense->dev,
+		 "Raspberry Pi Sense HAT firmware version %i\n", ret);
+
+	rpisense->joystick.pdev = rpisense_client_dev_register(rpisense,
+							       "rpi-sense-js");
+
+	if (IS_ERR(rpisense->joystick.pdev)) {
+		dev_err(rpisense->dev, "failed to register rpisense-js");
+		return PTR_ERR(rpisense->joystick.pdev);
+	}
+
+	rpisense->display.pdev = rpisense_client_dev_register(rpisense,
+								  "rpi-sense-fb");
+
+	if (IS_ERR(rpisense->display.pdev)) {
+		dev_err(rpisense->dev, "failed to register rpisense-fb");
+		return PTR_ERR(rpisense->display.pdev);
+	}
+
+	return 0;
+}
+
+static struct platform_device *
+rpisense_client_dev_register(struct rpisense *rpisense, const char *name)
+{
+	long ret = -ENOMEM;
+	struct platform_device *pdev = platform_device_alloc(name, -1);
+
+	if (pdev == NULL)
+		goto alloc_fail;
+
+	pdev->dev.parent = rpisense->dev;
+	platform_set_drvdata(pdev, rpisense);
+
+	ret = platform_device_add(pdev);
+	if (ret != 0)
+		goto add_fail;
+
+	ret = devm_add_action_or_reset(rpisense->dev,
+		(void *)platform_device_unregister, pdev);
+	if (ret != 0)
+		goto alloc_fail;
+
+	return pdev;
+
+add_fail:
+	platform_device_put(pdev);
+alloc_fail:
+	return ERR_PTR(ret);
+}
+
+int rpisense_get_joystick_state(struct rpisense *rpisense)
+{
+	int ret = i2c_smbus_read_byte_data(rpisense->i2c_client, RPISENSE_KEYS);
+
+	return ret < 0 ? ret : ret & 0x1f;
+}
+EXPORT_SYMBOL_GPL(rpisense_get_joystick_state);
+
+int rpisense_update_display(struct rpisense *rpisense)
+{
+	int i, j, ret;
+	struct rpisense_display *display = &rpisense->display;
+	struct {u8 reg, pixel_data[8][3][8]; } msg;
+
+	msg.reg = RPISENSE_DISPLAY;
+	for (i = 0; i < 8; ++i) {
+		for (j = 0; j < 8; ++j) {
+			msg.pixel_data[i][0][j] = display->gamma[display->vmem[i][j].r];
+			msg.pixel_data[i][1][j] = display->gamma[display->vmem[i][j].g];
+			msg.pixel_data[i][2][j] = display->gamma[display->vmem[i][j].b];
+		}
+	}
+
+	ret = i2c_master_send(rpisense->i2c_client, (u8 *)&msg, sizeof(msg));
+	if (ret < 0)
+		dev_err(rpisense->dev, "Update to 8x8 LED matrix display failed");
+	return ret;
+}
+EXPORT_SYMBOL_GPL(rpisense_update_display);
+
+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",
+	},
+	.probe = rpisense_probe,
+	.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/include/linux/mfd/rpisense.h b/include/linux/mfd/rpisense.h
new file mode 100644
index 000000000000..c2690ab9f820
--- /dev/null
+++ b/include/linux/mfd/rpisense.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Raspberry Pi Sense HAT core 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
+ */
+
+#ifndef __LINUX_MFD_RPISENSE_H_
+#define __LINUX_MFD_RPISENSE_H_
+#include <linux/miscdevice.h>
+
+#define SENSEDISP_IOC_MAGIC 0xF1
+
+#define SENSEDISP_IOGET_GAMMA _IO(SENSEDISP_IOC_MAGIC, 0)
+#define SENSEDISP_IOSET_GAMMA _IO(SENSEDISP_IOC_MAGIC, 1)
+#define SENSEDISP_IORESET_GAMMA _IO(SENSEDISP_IOC_MAGIC, 2)
+
+struct rpisense {
+	struct device *dev;
+	struct i2c_client *i2c_client;
+
+	/* Client devices */
+	struct rpisense_js {
+		struct platform_device *pdev;
+		struct input_dev *keys_dev;
+		struct gpio_desc *keys_desc;
+		int keys_irq;
+	} joystick;
+
+	struct rpisense_display {
+		struct platform_device *pdev;
+		struct miscdevice mdev;
+		struct mutex rw_mtx;
+		u8 gamma[32];
+		struct {
+			u16 b:5, u:1, g:5, r:5;
+		} vmem[8][8];
+	} display;
+};
+
+enum gamma_preset {
+	GAMMA_DEFAULT = 0,
+	GAMMA_LOWLIGHT,
+	GAMMA_PRESET_COUNT,
+};
+
+int rpisense_get_joystick_state(struct rpisense *rpisense);
+int rpisense_update_display(struct rpisense *rpisense);
+
+#endif
-- 
2.27.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH 2/3] drivers/mfd: rpisense: Raspberry Pi senseHAT joystick driver
  2021-08-07  0:27 [RFC PATCH 0/3] Raspberry Pi Sense HAT driver Joel Savitz
  2021-08-07  0:27 ` [RFC PATCH 1/3] drivers/mfd: rpisense: Raspberry Pi senseHAT core driver Joel Savitz
@ 2021-08-07  0:27 ` Joel Savitz
  2021-08-07 13:05   ` Peter Robinson
  2021-08-07  0:27 ` [RFC PATCH 3/3] drivers/mfd: rpisense: Raspberry Pi senseHAT 8x8 LED matrix display driver Joel Savitz
  2021-08-07  9:03 ` [RFC PATCH 0/3] Raspberry Pi Sense HAT driver Stefan Wahren
  3 siblings, 1 reply; 9+ messages in thread
From: Joel Savitz @ 2021-08-07  0:27 UTC (permalink / raw)
  To: linux-kernel
  Cc: Joel Savitz, Lee Jones, Stefan Wahren, Nicolas Saenz Julienne,
	linux-rpi-kernel, fedora-rpi, Charles Mirabile, Mwesigwa Guma

This patch implements support for the joystick, including a threaded
IRQ routine.

Signed-off-by: Charles Mirabile <cmirabil@redhat.com>
Signed-off-by: Mwesigwa Guma <mguma@redhat.com>
Signed-off-by: Joel Savitz <jsavitz@redhat.com>
---
 drivers/mfd/Makefile      |   1 +
 drivers/mfd/rpisense-js.c | 132 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 133 insertions(+)
 create mode 100644 drivers/mfd/rpisense-js.c

diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 76f9a9221241..71356153ccdc 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -264,6 +264,7 @@ obj-$(CONFIG_MFD_ROHM_BD957XMUF)	+= rohm-bd9576.o
 obj-$(CONFIG_MFD_STMFX) 	+= stmfx.o
 obj-$(CONFIG_MFD_KHADAS_MCU) 	+= khadas-mcu.o
 obj-$(CONFIG_MFD_RPISENSE) 	+= rpisense-core.o
+obj-$(CONFIG_MFD_RPISENSE)	+= rpisense-js.o
 obj-$(CONFIG_MFD_ACER_A500_EC)	+= acer-ec-a500.o
 obj-$(CONFIG_MFD_QCOM_PM8008)	+= qcom-pm8008.o
 
diff --git a/drivers/mfd/rpisense-js.c b/drivers/mfd/rpisense-js.c
new file mode 100644
index 000000000000..c6c97bff118c
--- /dev/null
+++ b/drivers/mfd/rpisense-js.c
@@ -0,0 +1,132 @@
+// 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/gpio/consumer.h>
+#include <linux/platform_device.h>
+
+#include <linux/mfd/rpisense.h>
+
+static unsigned char keymap[] = {KEY_DOWN, KEY_RIGHT, KEY_UP, KEY_ENTER, KEY_LEFT,};
+
+static irqreturn_t rpisense_js_report(int n, void *cookie)
+{
+	int i;
+	static s32 prev_keys;
+	struct rpisense *rpisense = cookie;
+	struct rpisense_js *rpisense_js = &rpisense->joystick;
+	s32 keys = rpisense_get_joystick_state(rpisense);
+	s32 changes = keys ^ prev_keys;
+
+	prev_keys = keys;
+	for (i = 0; i < ARRAY_SIZE(keymap); ++i) {
+		if (changes & (1<<i)) {
+			input_report_key(rpisense_js->keys_dev,
+					 keymap[i], keys & (1<<i));
+		}
+	}
+	input_sync(rpisense_js->keys_dev);
+	return IRQ_HANDLED;
+}
+
+static int rpisense_js_probe(struct platform_device *pdev)
+{
+	int ret;
+	int i;
+	struct rpisense *rpisense = dev_get_drvdata(&pdev->dev);
+	struct rpisense_js *rpisense_js = &rpisense->joystick;
+
+	rpisense_js->keys_desc = devm_gpiod_get(&rpisense->i2c_client->dev,
+						"keys-int", GPIOD_IN);
+	if (IS_ERR(rpisense_js->keys_desc)) {
+		dev_warn(&pdev->dev, "Failed to get keys-int descriptor.\n");
+		return PTR_ERR(rpisense_js->keys_desc);
+	}
+
+
+	rpisense_js->keys_dev = devm_input_allocate_device(&pdev->dev);
+	if (rpisense_js->keys_dev == NULL) {
+		dev_err(&pdev->dev, "Could not allocate input device.\n");
+		return -ENOMEM;
+	}
+
+	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");
+		return ret;
+	}
+
+	ret = gpiod_direction_input(rpisense_js->keys_desc);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not set keys-int direction.\n");
+		return ret;
+	}
+
+	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");
+		return rpisense_js->keys_irq;
+	}
+
+	ret = devm_request_threaded_irq(&pdev->dev, rpisense_js->keys_irq,
+		NULL, rpisense_js_report, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+		"keys", rpisense);
+
+	if (ret) {
+		dev_err(&pdev->dev, "IRQ request failed.\n");
+		return ret;
+	}
+	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,
+	.driver = {
+		.name = "rpi-sense-js",
+	},
+};
+
+module_platform_driver(rpisense_js_driver);
+
+MODULE_DESCRIPTION("Raspberry Pi Sense HAT joystick driver");
+MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
+MODULE_LICENSE("GPL");
-- 
2.27.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH 3/3] drivers/mfd: rpisense: Raspberry Pi senseHAT 8x8 LED matrix display driver
  2021-08-07  0:27 [RFC PATCH 0/3] Raspberry Pi Sense HAT driver Joel Savitz
  2021-08-07  0:27 ` [RFC PATCH 1/3] drivers/mfd: rpisense: Raspberry Pi senseHAT core driver Joel Savitz
  2021-08-07  0:27 ` [RFC PATCH 2/3] drivers/mfd: rpisense: Raspberry Pi senseHAT joystick driver Joel Savitz
@ 2021-08-07  0:27 ` Joel Savitz
  2021-08-07 13:20   ` Peter Robinson
  2021-08-07  9:03 ` [RFC PATCH 0/3] Raspberry Pi Sense HAT driver Stefan Wahren
  3 siblings, 1 reply; 9+ messages in thread
From: Joel Savitz @ 2021-08-07  0:27 UTC (permalink / raw)
  To: linux-kernel
  Cc: Joel Savitz, Lee Jones, Stefan Wahren, Nicolas Saenz Julienne,
	linux-rpi-kernel, fedora-rpi, Charles Mirabile, Mwesigwa Guma

This patch implements control of the 8x8 RGB LED matrix display.

Signed-off-by: Charles Mirabile <cmirabil@redhat.com>
Signed-off-by: Mwesigwa Guma <mguma@redhat.com>
Signed-off-by: Joel Savitz <jsavitz@redhat.com>
---
 drivers/mfd/Makefile           |   1 +
 drivers/mfd/rpisense-display.c | 242 +++++++++++++++++++++++++++++++++
 2 files changed, 243 insertions(+)
 create mode 100644 drivers/mfd/rpisense-display.c

diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 71356153ccdc..974518d9801b 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -265,6 +265,7 @@ obj-$(CONFIG_MFD_STMFX) 	+= stmfx.o
 obj-$(CONFIG_MFD_KHADAS_MCU) 	+= khadas-mcu.o
 obj-$(CONFIG_MFD_RPISENSE) 	+= rpisense-core.o
 obj-$(CONFIG_MFD_RPISENSE)	+= rpisense-js.o
+obj-$(CONFIG_MFD_RPISENSE)	+= rpisense-display.o
 obj-$(CONFIG_MFD_ACER_A500_EC)	+= acer-ec-a500.o
 obj-$(CONFIG_MFD_QCOM_PM8008)	+= qcom-pm8008.o
 
diff --git a/drivers/mfd/rpisense-display.c b/drivers/mfd/rpisense-display.c
new file mode 100644
index 000000000000..10a34fa0eece
--- /dev/null
+++ b/drivers/mfd/rpisense-display.c
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Raspberry Pi Sense HAT 8x8 LED matrix display 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/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/init.h>
+#include <linux/platform_device.h>
+#include <linux/mod_devicetable.h>
+
+#include <linux/mfd/rpisense.h>
+
+#define GAMMA_SIZE sizeof_field(struct rpisense_display, gamma)
+#define VMEM_SIZE sizeof_field(struct rpisense_display, vmem)
+
+static bool lowlight;
+module_param(lowlight, bool, 0);
+MODULE_PARM_DESC(lowlight, "Reduce LED matrix brightness to one third");
+
+static const u8 gamma_presets[][GAMMA_SIZE] = {
+	[GAMMA_DEFAULT] = {
+		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,
+	},
+	[GAMMA_LOWLIGHT] = {
+		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 const struct file_operations rpisense_display_fops;
+
+static int rpisense_display_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	struct rpisense *rpisense = dev_get_drvdata(&pdev->dev);
+	struct rpisense_display *rpisense_display = &rpisense->display;
+
+	memcpy(rpisense_display->gamma, gamma_presets[lowlight], GAMMA_SIZE);
+
+	memset(rpisense_display->vmem, 0, VMEM_SIZE);
+
+	mutex_init(&rpisense_display->rw_mtx);
+
+	rpisense_display->mdev = (struct miscdevice) {
+		.minor	= MISC_DYNAMIC_MINOR,
+		.name	= "sense-hat",
+		.mode	= 0666,
+		.fops	= &rpisense_display_fops,
+	};
+
+	ret = misc_register(&rpisense_display->mdev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Could not register 8x8 LED matrix display.\n");
+		return ret;
+	}
+
+	dev_info(&pdev->dev, "8x8 LED matrix display registered with minor number %i",
+		rpisense_display->mdev.minor);
+
+	rpisense_update_display(rpisense);
+	return 0;
+}
+
+static int rpisense_display_remove(struct platform_device *pdev)
+{
+	struct rpisense *rpisense = dev_get_drvdata(&pdev->dev);
+	struct rpisense_display *rpisense_display = &rpisense->display;
+
+	misc_deregister(&rpisense_display->mdev);
+	return 0;
+}
+
+static loff_t rpisense_display_llseek(struct file *filp, loff_t pos, int whence)
+{
+	loff_t base;
+
+	switch (whence) {
+	case SEEK_SET:
+		base = 0;
+		break;
+	case SEEK_CUR:
+		base = filp->f_pos;
+		break;
+	case SEEK_END:
+		base = VMEM_SIZE;
+		break;
+	default:
+		return -EINVAL;
+	}
+	base += pos;
+	if (base < 0 || base >= VMEM_SIZE)
+		return -EINVAL;
+	filp->f_pos = base;
+	return base;
+}
+
+static ssize_t
+rpisense_display_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
+{
+	struct rpisense *rpisense = container_of(filp->private_data, struct rpisense, display.mdev);
+	struct rpisense_display *rpisense_display = &rpisense->display;
+	ssize_t retval = -EFAULT;
+
+	if (*f_pos >= VMEM_SIZE)
+		return 0;
+	if (*f_pos + count > VMEM_SIZE)
+		count = VMEM_SIZE - *f_pos;
+	if (mutex_lock_interruptible(&rpisense_display->rw_mtx))
+		return -ERESTARTSYS;
+	if (copy_to_user(buf, rpisense_display->vmem + *f_pos, count))
+		goto out;
+	*f_pos += count;
+	retval = count;
+out:
+	mutex_unlock(&rpisense_display->rw_mtx);
+	return retval;
+}
+
+static ssize_t
+rpisense_display_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
+{
+	struct rpisense *rpisense = container_of(filp->private_data, struct rpisense, display.mdev);
+	struct rpisense_display *rpisense_display = &rpisense->display;
+	u8 temp[VMEM_SIZE];
+
+	if (*f_pos >= VMEM_SIZE)
+		return -EFBIG;
+	if (*f_pos + count > VMEM_SIZE)
+		count = VMEM_SIZE - *f_pos;
+	if (copy_from_user(temp, buf, count))
+		return -EFAULT;
+	if (mutex_lock_interruptible(&rpisense_display->rw_mtx))
+		return -ERESTARTSYS;
+	memcpy(rpisense_display->vmem + *f_pos, temp, count);
+	rpisense_update_display(rpisense);
+	*f_pos += count;
+	mutex_unlock(&rpisense_display->rw_mtx);
+	return count;
+}
+
+static long rpisense_display_ioctl(struct file *filp, unsigned int cmd,
+			     unsigned long arg)
+{
+	struct rpisense *rpisense = container_of(filp->private_data, struct rpisense, display.mdev);
+	struct rpisense_display *rpisense_display = &rpisense->display;
+	void __user *user_ptr = (void __user *)arg;
+	u8 temp[GAMMA_SIZE];
+	int ret;
+
+	if (mutex_lock_interruptible(&rpisense_display->rw_mtx))
+		return -ERESTARTSYS;
+	switch (cmd) {
+	case SENSEDISP_IOGET_GAMMA:
+		if (copy_to_user(user_ptr, rpisense_display->gamma, GAMMA_SIZE)) {
+			ret = -EFAULT;
+			goto out_unlock;
+		}
+		ret = 0;
+		goto out_unlock;
+	case SENSEDISP_IOSET_GAMMA:
+		if (copy_from_user(temp, user_ptr, GAMMA_SIZE)) {
+			ret = -EFAULT;
+			goto out_unlock;
+		}
+		ret = 0;
+		goto out_update;
+	case SENSEDISP_IORESET_GAMMA:
+		if (arg < GAMMA_DEFAULT || arg >= GAMMA_PRESET_COUNT) {
+			ret = -EINVAL;
+			goto out_unlock;
+		}
+		memcpy(temp, gamma_presets[arg], GAMMA_SIZE);
+		ret = 0;
+		goto out_update;
+	default:
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+out_update:
+	memcpy(rpisense_display->gamma, temp, GAMMA_SIZE);
+	rpisense_update_display(rpisense);
+out_unlock:
+	mutex_unlock(&rpisense_display->rw_mtx);
+	return ret;
+}
+
+static const struct file_operations rpisense_display_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= rpisense_display_llseek,
+	.read		= rpisense_display_read,
+	.write		= rpisense_display_write,
+	.unlocked_ioctl	= rpisense_display_ioctl,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id rpisense_display_id[] = {
+	{ .compatible = "rpi,rpi-sense-fb" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, rpisense_display_id);
+#endif
+
+static struct platform_device_id rpisense_display_device_id[] = {
+	{ .name = "rpi-sense-fb" },
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, rpisense_display_device_id);
+
+static struct platform_driver rpisense_display_driver = {
+	.probe = rpisense_display_probe,
+	.remove = rpisense_display_remove,
+	.driver = {
+		.name = "rpi-sense-fb",
+	},
+};
+
+module_platform_driver(rpisense_display_driver);
+
+MODULE_DESCRIPTION("Raspberry Pi Sense HAT 8x8 LED matrix display driver");
+MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
+MODULE_LICENSE("GPL");
+
-- 
2.27.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH 1/3] drivers/mfd: rpisense: Raspberry Pi senseHAT core driver
  2021-08-07  0:27 ` [RFC PATCH 1/3] drivers/mfd: rpisense: Raspberry Pi senseHAT core driver Joel Savitz
@ 2021-08-07  0:32   ` Randy Dunlap
  2021-08-07  9:53   ` Stefan Wahren
  1 sibling, 0 replies; 9+ messages in thread
From: Randy Dunlap @ 2021-08-07  0:32 UTC (permalink / raw)
  To: Joel Savitz, linux-kernel
  Cc: Lee Jones, Stefan Wahren, Nicolas Saenz Julienne,
	linux-rpi-kernel, fedora-rpi, Charles Mirabile, Mwesigwa Guma

On 8/6/21 5:27 PM, Joel Savitz wrote:
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 6a3fd2d75f96..614de080dee6 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -11,6 +11,16 @@ config MFD_CORE
>   	select IRQ_DOMAIN
>   	default n
>   
> +config MFD_RPISENSE
> +	tristate "Raspberry Pi Sense HAT driver"
> +	depends on I2C && GPIOLIB
> +	select MFD_CORE
> +	help
> +	  This is the driver for the Raspberry Pi Sense HAT. This provides
> +	  the necessary functions to communicate with the hardware as well
> +	  as a joystick and display interface. Linux communicates with the
> +	  hardwire using the GPIO pins via the I2C protocol.

	  hardware


What is HAT, please?

-- 
~Randy


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH 0/3] Raspberry Pi Sense HAT driver
  2021-08-07  0:27 [RFC PATCH 0/3] Raspberry Pi Sense HAT driver Joel Savitz
                   ` (2 preceding siblings ...)
  2021-08-07  0:27 ` [RFC PATCH 3/3] drivers/mfd: rpisense: Raspberry Pi senseHAT 8x8 LED matrix display driver Joel Savitz
@ 2021-08-07  9:03 ` Stefan Wahren
  3 siblings, 0 replies; 9+ messages in thread
From: Stefan Wahren @ 2021-08-07  9:03 UTC (permalink / raw)
  To: Joel Savitz, linux-kernel
  Cc: Lee Jones, Nicolas Saenz Julienne, linux-rpi-kernel, fedora-rpi,
	Charles Mirabile, Mwesigwa Guma

Hi Joel,

Am 07.08.21 um 02:27 schrieb Joel Savitz:
> This patch series adds a set of drivers for operating the Sense HAT
> peripheral device. This board is an add on for the Raspberry Pi that is
> designed to connect using the GPIO connector via I2C.
>
> It features:
> 	- a joystick
> 	- an 8x8 RGB LED matrix display
> 	- a whole bunch of environmental sensors with their own drivers
> 	(those are already in upstream Linux)
>
> This is a refactor of the work of Serge Schneider, the author of a
> version of this driver that is currently in the Raspberry Pi downstream
> kernel. We modified his code to make it suitable for upstream Linux.
>
> A couple of tests are available for this driver in this repo:
> https://github.com/underground-software/sensehat/tree/master/tests
> 	- color_test displays various solid colors on the LED panel
> 	- framebuffer_test diplays a single lit cell that is
> 	  controllable via the arrow keys or the joystick
>
> Though the driver is split into three modules, they are interdependent
> and meaningless in isolation. As such, we have grouped them together.
>
> This is a request for comments. Please let us know if you
> have any suggestions, complements, or harsh criticism. ;)
>
> Known issue:
> - We do not know how to integrate the device tree overlay into mainline
>   Linux. Fortunately, most Linux distrubtions designed for the Raspberry
>   Pi already include the approriate device tree overlay as a part of
>   their importation of all overlays from the downstream Raspberry Pi
>   kernel. Suggestions are welcome and appreciated.
>
AFAIK the overlay support isn't complete yet. In order to give a chance
to test with mainline, developers provided patches against a specific
board DTS (e.g. RPi 3B) and tag them as DO NOT MERGE.


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH 1/3] drivers/mfd: rpisense: Raspberry Pi senseHAT core driver
  2021-08-07  0:27 ` [RFC PATCH 1/3] drivers/mfd: rpisense: Raspberry Pi senseHAT core driver Joel Savitz
  2021-08-07  0:32   ` Randy Dunlap
@ 2021-08-07  9:53   ` Stefan Wahren
  1 sibling, 0 replies; 9+ messages in thread
From: Stefan Wahren @ 2021-08-07  9:53 UTC (permalink / raw)
  To: Joel Savitz, linux-kernel
  Cc: Lee Jones, Nicolas Saenz Julienne, linux-rpi-kernel, fedora-rpi,
	Charles Mirabile, Mwesigwa Guma

Am 07.08.21 um 02:27 schrieb Joel Savitz:
> This patch adds the core driver file, containing methods to communicate
> with the board over I2C. We also add the header file shared by all
> three drivers, containing common data and definitions. In addition, we
> add a config option to toggle compilation of the driver.
>
> Signed-off-by: Charles Mirabile <cmirabil@redhat.com>
> Signed-off-by: Mwesigwa Guma <mguma@redhat.com>
> Signed-off-by: Joel Savitz <jsavitz@redhat.com>
> ---
>  drivers/mfd/Kconfig          |  10 +++
>  drivers/mfd/Makefile         |   1 +
>  drivers/mfd/rpisense-core.c  | 170 +++++++++++++++++++++++++++++++++++
>  include/linux/mfd/rpisense.h |  55 ++++++++++++
>  4 files changed, 236 insertions(+)
>  create mode 100644 drivers/mfd/rpisense-core.c
>  create mode 100644 include/linux/mfd/rpisense.h
>
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 6a3fd2d75f96..614de080dee6 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -11,6 +11,16 @@ config MFD_CORE
>  	select IRQ_DOMAIN
>  	default n
>  
> +config MFD_RPISENSE
RPI is not used before in config options. I suggest MFG_RASPBERRYPI_SENSE
> +	tristate "Raspberry Pi Sense HAT driver"
> +	depends on I2C && GPIOLIB
> +	select MFD_CORE
> +	help
> +	  This is the driver for the Raspberry Pi Sense HAT. This provides
> +	  the necessary functions to communicate with the hardware as well
> +	  as a joystick and display interface. Linux communicates with the
> +	  hardwire using the GPIO pins via the I2C protocol.
> +
>  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 8116c19d5fd4..76f9a9221241 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -263,6 +263,7 @@ obj-$(CONFIG_MFD_ROHM_BD718XX)	+= rohm-bd718x7.o
>  obj-$(CONFIG_MFD_ROHM_BD957XMUF)	+= rohm-bd9576.o
>  obj-$(CONFIG_MFD_STMFX) 	+= stmfx.o
>  obj-$(CONFIG_MFD_KHADAS_MCU) 	+= khadas-mcu.o
> +obj-$(CONFIG_MFD_RPISENSE) 	+= rpisense-core.o
>  obj-$(CONFIG_MFD_ACER_A500_EC)	+= acer-ec-a500.o
>  obj-$(CONFIG_MFD_QCOM_PM8008)	+= qcom-pm8008.o
>  
> diff --git a/drivers/mfd/rpisense-core.c b/drivers/mfd/rpisense-core.c
> new file mode 100644
> index 000000000000..69e3051a4be0
> --- /dev/null
> +++ b/drivers/mfd/rpisense-core.c
> @@ -0,0 +1,170 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Raspberry Pi Sense HAT core 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
> + *
> + * This driver is based on wm8350 implementation and was refactored to use the
> + * misc device subsystem rather than the deprecated framebuffer subsystem.
> + */
> +
> +#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/slab.h>
> +#include <linux/mfd/rpisense.h>
> +
> +#define RPISENSE_DISPLAY		0x00
> +#define RPISENSE_WAI			0xF0
> +#define RPISENSE_VER			0xF1
> +#define RPISENSE_KEYS			0xF2
> +#define RPISENSE_EE_WP			0xF3
> +
> +#define RPISENSE_ID			's'
> +
> +static struct platform_device *
> +rpisense_client_dev_register(struct rpisense *rpisense, const char *name);
> +
> +static int rpisense_probe(struct i2c_client *i2c,
> +			       const struct i2c_device_id *id)
> +{
> +	int ret;
> +
> +	struct rpisense *rpisense = devm_kzalloc(&i2c->dev, sizeof(*rpisense), GFP_KERNEL);
> +
> +	if (rpisense == NULL)
> +		return -ENOMEM;
The more common style is: if (!rpisense)
> +
> +	i2c_set_clientdata(i2c, rpisense);
> +	rpisense->dev = &i2c->dev;
> +	rpisense->i2c_client = i2c;
> +
> +
> +	ret = i2c_smbus_read_byte_data(rpisense->i2c_client, RPISENSE_WAI);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (ret != RPISENSE_ID)
> +		return -EINVAL;
> +
> +	ret = i2c_smbus_read_byte_data(rpisense->i2c_client, RPISENSE_VER);
> +	if (ret < 0)
> +		return ret;
Not sure about all the error cases, but i think the last two deserve a
log entry.
> +
> +	dev_info(rpisense->dev,
> +		 "Raspberry Pi Sense HAT firmware version %i\n", ret);
> +
> +	rpisense->joystick.pdev = rpisense_client_dev_register(rpisense,
> +							       "rpi-sense-js");
> +
> +	if (IS_ERR(rpisense->joystick.pdev)) {
> +		dev_err(rpisense->dev, "failed to register rpisense-js");
> +		return PTR_ERR(rpisense->joystick.pdev);
> +	}
> +
> +	rpisense->display.pdev = rpisense_client_dev_register(rpisense,
> +								  "rpi-sense-fb");
> +
> +	if (IS_ERR(rpisense->display.pdev)) {
> +		dev_err(rpisense->dev, "failed to register rpisense-fb");
> +		return PTR_ERR(rpisense->display.pdev);
> +	}
> +
> +	return 0;
> +}
> +
> +static struct platform_device *
> +rpisense_client_dev_register(struct rpisense *rpisense, const char *name)
> +{
> +	long ret = -ENOMEM;
> +	struct platform_device *pdev = platform_device_alloc(name, -1);
> +
> +	if (pdev == NULL)
> +		goto alloc_fail;
> +
> +	pdev->dev.parent = rpisense->dev;
> +	platform_set_drvdata(pdev, rpisense);
> +
> +	ret = platform_device_add(pdev);
> +	if (ret != 0)
Similiar as above: if (ret)
> +		goto add_fail;
> +
> +	ret = devm_add_action_or_reset(rpisense->dev,
> +		(void *)platform_device_unregister, pdev);
> +	if (ret != 0)
> +		goto alloc_fail;
> +
> +	return pdev;
> +
> +add_fail:
> +	platform_device_put(pdev);
> +alloc_fail:
> +	return ERR_PTR(ret);
> +}
> +
> +int rpisense_get_joystick_state(struct rpisense *rpisense)
> +{
> +	int ret = i2c_smbus_read_byte_data(rpisense->i2c_client, RPISENSE_KEYS);
> +
> +	return ret < 0 ? ret : ret & 0x1f;
> +}
> +EXPORT_SYMBOL_GPL(rpisense_get_joystick_state);
> +
> +int rpisense_update_display(struct rpisense *rpisense)
> +{
> +	int i, j, ret;
> +	struct rpisense_display *display = &rpisense->display;
> +	struct {u8 reg, pixel_data[8][3][8]; } msg;
> +
> +	msg.reg = RPISENSE_DISPLAY;
> +	for (i = 0; i < 8; ++i) {
> +		for (j = 0; j < 8; ++j) {
> +			msg.pixel_data[i][0][j] = display->gamma[display->vmem[i][j].r];
> +			msg.pixel_data[i][1][j] = display->gamma[display->vmem[i][j].g];
> +			msg.pixel_data[i][2][j] = display->gamma[display->vmem[i][j].b];
> +		}
> +	}
> +
> +	ret = i2c_master_send(rpisense->i2c_client, (u8 *)&msg, sizeof(msg));
> +	if (ret < 0)
> +		dev_err(rpisense->dev, "Update to 8x8 LED matrix display failed");
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(rpisense_update_display);
> +
> +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" },

The vendor is raspberrypi, which is already listed in Documentation
<https://elixir.bootlin.com/linux/latest/source/Documentation>/devicetree
<https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree>/bindings
<https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings>/vendor-prefixes.yaml
<https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings/vendor-prefixes.yaml>

Not sure about the compatible. It must be added to the bindings, but
unsure this should go to Documentation
<https://elixir.bootlin.com/linux/latest/source/Documentation>/devicetree
<https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree>/bindings
<https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings>/trivial-devices.yaml
<https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings/trivial-devices.yaml>
or a separate YAML file.

Best regards




^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH 2/3] drivers/mfd: rpisense: Raspberry Pi senseHAT joystick driver
  2021-08-07  0:27 ` [RFC PATCH 2/3] drivers/mfd: rpisense: Raspberry Pi senseHAT joystick driver Joel Savitz
@ 2021-08-07 13:05   ` Peter Robinson
  0 siblings, 0 replies; 9+ messages in thread
From: Peter Robinson @ 2021-08-07 13:05 UTC (permalink / raw)
  To: Joel Savitz
  Cc: linux-kernel, fedora-rpi, Mwesigwa Guma, Charles Mirabile,
	linux-rpi-kernel, Lee Jones

On Sat, Aug 7, 2021 at 1:31 AM Joel Savitz <jsavitz@redhat.com> wrote:
>
> This patch implements support for the joystick, including a threaded
> IRQ routine.

If you look at this on it's own, remember the cover letter doesn't go
into git, it's not overly descriptive. We know it's a patch. What does
the "threaded IRQ routine" do in the context, why is that specifically
called out.

Something like:
A simple joystick with up/down/left/right and enter attached via i2c

> Signed-off-by: Charles Mirabile <cmirabil@redhat.com>
> Signed-off-by: Mwesigwa Guma <mguma@redhat.com>
> Signed-off-by: Joel Savitz <jsavitz@redhat.com>
> ---
>  drivers/mfd/Makefile      |   1 +
>  drivers/mfd/rpisense-js.c | 132 ++++++++++++++++++++++++++++++++++++++

This driver should be in drivers/input/joystick/

>  2 files changed, 133 insertions(+)
>  create mode 100644 drivers/mfd/rpisense-js.c
>
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 76f9a9221241..71356153ccdc 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -264,6 +264,7 @@ obj-$(CONFIG_MFD_ROHM_BD957XMUF)    += rohm-bd9576.o
>  obj-$(CONFIG_MFD_STMFX)        += stmfx.o
>  obj-$(CONFIG_MFD_KHADAS_MCU)   += khadas-mcu.o
>  obj-$(CONFIG_MFD_RPISENSE)     += rpisense-core.o
> +obj-$(CONFIG_MFD_RPISENSE)     += rpisense-js.o

While it's unlikely that these will be used without one another I
still believe this needs it's own Kconfig entry.

>  obj-$(CONFIG_MFD_ACER_A500_EC) += acer-ec-a500.o
>  obj-$(CONFIG_MFD_QCOM_PM8008)  += qcom-pm8008.o
>
> diff --git a/drivers/mfd/rpisense-js.c b/drivers/mfd/rpisense-js.c
> new file mode 100644
> index 000000000000..c6c97bff118c
> --- /dev/null
> +++ b/drivers/mfd/rpisense-js.c
> @@ -0,0 +1,132 @@
> +// 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/gpio/consumer.h>
> +#include <linux/platform_device.h>
> +
> +#include <linux/mfd/rpisense.h>
> +
> +static unsigned char keymap[] = {KEY_DOWN, KEY_RIGHT, KEY_UP, KEY_ENTER, KEY_LEFT,};
> +
> +static irqreturn_t rpisense_js_report(int n, void *cookie)
> +{
> +       int i;
> +       static s32 prev_keys;
> +       struct rpisense *rpisense = cookie;
> +       struct rpisense_js *rpisense_js = &rpisense->joystick;
> +       s32 keys = rpisense_get_joystick_state(rpisense);
> +       s32 changes = keys ^ prev_keys;
> +
> +       prev_keys = keys;
> +       for (i = 0; i < ARRAY_SIZE(keymap); ++i) {
> +               if (changes & (1<<i)) {
> +                       input_report_key(rpisense_js->keys_dev,
> +                                        keymap[i], keys & (1<<i));
> +               }
> +       }
> +       input_sync(rpisense_js->keys_dev);
> +       return IRQ_HANDLED;
> +}
> +
> +static int rpisense_js_probe(struct platform_device *pdev)
> +{
> +       int ret;
> +       int i;
> +       struct rpisense *rpisense = dev_get_drvdata(&pdev->dev);
> +       struct rpisense_js *rpisense_js = &rpisense->joystick;
> +
> +       rpisense_js->keys_desc = devm_gpiod_get(&rpisense->i2c_client->dev,
> +                                               "keys-int", GPIOD_IN);
> +       if (IS_ERR(rpisense_js->keys_desc)) {
> +               dev_warn(&pdev->dev, "Failed to get keys-int descriptor.\n");
> +               return PTR_ERR(rpisense_js->keys_desc);
> +       }
> +
> +
> +       rpisense_js->keys_dev = devm_input_allocate_device(&pdev->dev);
> +       if (rpisense_js->keys_dev == NULL) {
> +               dev_err(&pdev->dev, "Could not allocate input device.\n");
> +               return -ENOMEM;
> +       }
> +
> +       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");
> +               return ret;
> +       }
> +
> +       ret = gpiod_direction_input(rpisense_js->keys_desc);
> +       if (ret) {
> +               dev_err(&pdev->dev, "Could not set keys-int direction.\n");
> +               return ret;
> +       }
> +
> +       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");
> +               return rpisense_js->keys_irq;
> +       }
> +
> +       ret = devm_request_threaded_irq(&pdev->dev, rpisense_js->keys_irq,
> +               NULL, rpisense_js_report, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> +               "keys", rpisense);
> +
> +       if (ret) {
> +               dev_err(&pdev->dev, "IRQ request failed.\n");
> +               return ret;
> +       }
> +       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,
> +       .driver = {
> +               .name = "rpi-sense-js",
> +       },
> +};
> +
> +module_platform_driver(rpisense_js_driver);
> +
> +MODULE_DESCRIPTION("Raspberry Pi Sense HAT joystick driver");
> +MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");

If you're leaving Serge as the module author should be copied in the
patch series.

> +MODULE_LICENSE("GPL");
> --
> 2.27.0
>
>
> _______________________________________________
> linux-rpi-kernel mailing list
> linux-rpi-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rpi-kernel

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH 3/3] drivers/mfd: rpisense: Raspberry Pi senseHAT 8x8 LED matrix display driver
  2021-08-07  0:27 ` [RFC PATCH 3/3] drivers/mfd: rpisense: Raspberry Pi senseHAT 8x8 LED matrix display driver Joel Savitz
@ 2021-08-07 13:20   ` Peter Robinson
  0 siblings, 0 replies; 9+ messages in thread
From: Peter Robinson @ 2021-08-07 13:20 UTC (permalink / raw)
  To: Joel Savitz
  Cc: linux-kernel, fedora-rpi, Mwesigwa Guma, Charles Mirabile,
	linux-rpi-kernel, Lee Jones

On Sat, Aug 7, 2021 at 1:31 AM Joel Savitz <jsavitz@redhat.com> wrote:
>
> This patch implements control of the 8x8 RGB LED matrix display.
>
> Signed-off-by: Charles Mirabile <cmirabil@redhat.com>
> Signed-off-by: Mwesigwa Guma <mguma@redhat.com>
> Signed-off-by: Joel Savitz <jsavitz@redhat.com>
> ---
>  drivers/mfd/Makefile           |   1 +
>  drivers/mfd/rpisense-display.c | 242 +++++++++++++++++++++++++++++++++

I suspect this better fits in drivers/auxdisplay/

>  2 files changed, 243 insertions(+)
>  create mode 100644 drivers/mfd/rpisense-display.c
>
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 71356153ccdc..974518d9801b 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -265,6 +265,7 @@ obj-$(CONFIG_MFD_STMFX)     += stmfx.o
>  obj-$(CONFIG_MFD_KHADAS_MCU)   += khadas-mcu.o
>  obj-$(CONFIG_MFD_RPISENSE)     += rpisense-core.o
>  obj-$(CONFIG_MFD_RPISENSE)     += rpisense-js.o
> +obj-$(CONFIG_MFD_RPISENSE)     += rpisense-display.o
>  obj-$(CONFIG_MFD_ACER_A500_EC) += acer-ec-a500.o
>  obj-$(CONFIG_MFD_QCOM_PM8008)  += qcom-pm8008.o
>
> diff --git a/drivers/mfd/rpisense-display.c b/drivers/mfd/rpisense-display.c
> new file mode 100644
> index 000000000000..10a34fa0eece
> --- /dev/null
> +++ b/drivers/mfd/rpisense-display.c
> @@ -0,0 +1,242 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Raspberry Pi Sense HAT 8x8 LED matrix display 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/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/init.h>
> +#include <linux/platform_device.h>
> +#include <linux/mod_devicetable.h>
> +
> +#include <linux/mfd/rpisense.h>
> +
> +#define GAMMA_SIZE sizeof_field(struct rpisense_display, gamma)
> +#define VMEM_SIZE sizeof_field(struct rpisense_display, vmem)
> +
> +static bool lowlight;
> +module_param(lowlight, bool, 0);
> +MODULE_PARM_DESC(lowlight, "Reduce LED matrix brightness to one third");
> +
> +static const u8 gamma_presets[][GAMMA_SIZE] = {
> +       [GAMMA_DEFAULT] = {
> +               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,
> +       },
> +       [GAMMA_LOWLIGHT] = {
> +               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 const struct file_operations rpisense_display_fops;
> +
> +static int rpisense_display_probe(struct platform_device *pdev)
> +{
> +       int ret;
> +
> +       struct rpisense *rpisense = dev_get_drvdata(&pdev->dev);
> +       struct rpisense_display *rpisense_display = &rpisense->display;
> +
> +       memcpy(rpisense_display->gamma, gamma_presets[lowlight], GAMMA_SIZE);
> +
> +       memset(rpisense_display->vmem, 0, VMEM_SIZE);
> +
> +       mutex_init(&rpisense_display->rw_mtx);
> +
> +       rpisense_display->mdev = (struct miscdevice) {
> +               .minor  = MISC_DYNAMIC_MINOR,
> +               .name   = "sense-hat",
> +               .mode   = 0666,
> +               .fops   = &rpisense_display_fops,
> +       };
> +
> +       ret = misc_register(&rpisense_display->mdev);
> +       if (ret < 0) {
> +               dev_err(&pdev->dev, "Could not register 8x8 LED matrix display.\n");
> +               return ret;
> +       }
> +
> +       dev_info(&pdev->dev, "8x8 LED matrix display registered with minor number %i",
> +               rpisense_display->mdev.minor);
> +
> +       rpisense_update_display(rpisense);
> +       return 0;
> +}
> +
> +static int rpisense_display_remove(struct platform_device *pdev)
> +{
> +       struct rpisense *rpisense = dev_get_drvdata(&pdev->dev);
> +       struct rpisense_display *rpisense_display = &rpisense->display;
> +
> +       misc_deregister(&rpisense_display->mdev);
> +       return 0;
> +}
> +
> +static loff_t rpisense_display_llseek(struct file *filp, loff_t pos, int whence)
> +{
> +       loff_t base;
> +
> +       switch (whence) {
> +       case SEEK_SET:
> +               base = 0;
> +               break;
> +       case SEEK_CUR:
> +               base = filp->f_pos;
> +               break;
> +       case SEEK_END:
> +               base = VMEM_SIZE;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +       base += pos;
> +       if (base < 0 || base >= VMEM_SIZE)
> +               return -EINVAL;
> +       filp->f_pos = base;
> +       return base;
> +}
> +
> +static ssize_t
> +rpisense_display_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
> +{
> +       struct rpisense *rpisense = container_of(filp->private_data, struct rpisense, display.mdev);
> +       struct rpisense_display *rpisense_display = &rpisense->display;
> +       ssize_t retval = -EFAULT;
> +
> +       if (*f_pos >= VMEM_SIZE)
> +               return 0;
> +       if (*f_pos + count > VMEM_SIZE)
> +               count = VMEM_SIZE - *f_pos;
> +       if (mutex_lock_interruptible(&rpisense_display->rw_mtx))
> +               return -ERESTARTSYS;
> +       if (copy_to_user(buf, rpisense_display->vmem + *f_pos, count))
> +               goto out;
> +       *f_pos += count;
> +       retval = count;
> +out:
> +       mutex_unlock(&rpisense_display->rw_mtx);
> +       return retval;
> +}
> +
> +static ssize_t
> +rpisense_display_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
> +{
> +       struct rpisense *rpisense = container_of(filp->private_data, struct rpisense, display.mdev);
> +       struct rpisense_display *rpisense_display = &rpisense->display;
> +       u8 temp[VMEM_SIZE];
> +
> +       if (*f_pos >= VMEM_SIZE)
> +               return -EFBIG;
> +       if (*f_pos + count > VMEM_SIZE)
> +               count = VMEM_SIZE - *f_pos;
> +       if (copy_from_user(temp, buf, count))
> +               return -EFAULT;
> +       if (mutex_lock_interruptible(&rpisense_display->rw_mtx))
> +               return -ERESTARTSYS;
> +       memcpy(rpisense_display->vmem + *f_pos, temp, count);
> +       rpisense_update_display(rpisense);
> +       *f_pos += count;
> +       mutex_unlock(&rpisense_display->rw_mtx);
> +       return count;
> +}
> +
> +static long rpisense_display_ioctl(struct file *filp, unsigned int cmd,
> +                            unsigned long arg)
> +{
> +       struct rpisense *rpisense = container_of(filp->private_data, struct rpisense, display.mdev);
> +       struct rpisense_display *rpisense_display = &rpisense->display;
> +       void __user *user_ptr = (void __user *)arg;
> +       u8 temp[GAMMA_SIZE];
> +       int ret;
> +
> +       if (mutex_lock_interruptible(&rpisense_display->rw_mtx))
> +               return -ERESTARTSYS;
> +       switch (cmd) {
> +       case SENSEDISP_IOGET_GAMMA:
> +               if (copy_to_user(user_ptr, rpisense_display->gamma, GAMMA_SIZE)) {
> +                       ret = -EFAULT;
> +                       goto out_unlock;
> +               }
> +               ret = 0;
> +               goto out_unlock;
> +       case SENSEDISP_IOSET_GAMMA:
> +               if (copy_from_user(temp, user_ptr, GAMMA_SIZE)) {
> +                       ret = -EFAULT;
> +                       goto out_unlock;
> +               }
> +               ret = 0;
> +               goto out_update;
> +       case SENSEDISP_IORESET_GAMMA:
> +               if (arg < GAMMA_DEFAULT || arg >= GAMMA_PRESET_COUNT) {
> +                       ret = -EINVAL;
> +                       goto out_unlock;
> +               }
> +               memcpy(temp, gamma_presets[arg], GAMMA_SIZE);
> +               ret = 0;
> +               goto out_update;
> +       default:
> +               ret = -EINVAL;
> +               goto out_unlock;
> +       }
> +out_update:
> +       memcpy(rpisense_display->gamma, temp, GAMMA_SIZE);
> +       rpisense_update_display(rpisense);
> +out_unlock:
> +       mutex_unlock(&rpisense_display->rw_mtx);
> +       return ret;
> +}
> +
> +static const struct file_operations rpisense_display_fops = {
> +       .owner          = THIS_MODULE,
> +       .llseek         = rpisense_display_llseek,
> +       .read           = rpisense_display_read,
> +       .write          = rpisense_display_write,
> +       .unlocked_ioctl = rpisense_display_ioctl,
> +};
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id rpisense_display_id[] = {
> +       { .compatible = "rpi,rpi-sense-fb" },
> +       { },
> +};
> +MODULE_DEVICE_TABLE(of, rpisense_display_id);
> +#endif
> +
> +static struct platform_device_id rpisense_display_device_id[] = {
> +       { .name = "rpi-sense-fb" },

I don't think -fb is the best name for this device, it's not a
framebuffer display/driver.

> +       { },
> +};
> +MODULE_DEVICE_TABLE(platform, rpisense_display_device_id);
> +
> +static struct platform_driver rpisense_display_driver = {
> +       .probe = rpisense_display_probe,
> +       .remove = rpisense_display_remove,
> +       .driver = {
> +               .name = "rpi-sense-fb",
> +       },
> +};
> +
> +module_platform_driver(rpisense_display_driver);
> +
> +MODULE_DESCRIPTION("Raspberry Pi Sense HAT 8x8 LED matrix display driver");

Again include the original author in the cc:

> +MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
> +MODULE_LICENSE("GPL");
> +
> --
> 2.27.0
>
>
> _______________________________________________
> linux-rpi-kernel mailing list
> linux-rpi-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rpi-kernel

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2021-08-07 13:20 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-07  0:27 [RFC PATCH 0/3] Raspberry Pi Sense HAT driver Joel Savitz
2021-08-07  0:27 ` [RFC PATCH 1/3] drivers/mfd: rpisense: Raspberry Pi senseHAT core driver Joel Savitz
2021-08-07  0:32   ` Randy Dunlap
2021-08-07  9:53   ` Stefan Wahren
2021-08-07  0:27 ` [RFC PATCH 2/3] drivers/mfd: rpisense: Raspberry Pi senseHAT joystick driver Joel Savitz
2021-08-07 13:05   ` Peter Robinson
2021-08-07  0:27 ` [RFC PATCH 3/3] drivers/mfd: rpisense: Raspberry Pi senseHAT 8x8 LED matrix display driver Joel Savitz
2021-08-07 13:20   ` Peter Robinson
2021-08-07  9:03 ` [RFC PATCH 0/3] Raspberry Pi Sense HAT driver Stefan Wahren

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).