All of lore.kernel.org
 help / color / mirror / Atom feed
From: Charles Mirabile <cmirabil@redhat.com>
To: linux-kernel@vger.kernel.org
Cc: Charles Mirabile <cmirabil@redhat.com>,
	Miguel Ojeda <ojeda@kernel.org>,
	Serge Schneider <serge@raspberrypi.org>,
	Stefan Wahren <stefan.wahren@i2se.com>,
	Nicolas Saenz Julienne <nsaenzju@redhat.com>,
	linux-rpi-kernel@lists.infradead.org,
	fedora-rpi@googlegroups.com, Mwesigwa Guma <mguma@redhat.com>,
	Joel Savitz <jsavitz@redhat.com>
Subject: [PATCH 3/5] drivers/auxdisplay: senshat: Raspberry Pi Sense HAT display driver
Date: Fri, 29 Oct 2021 17:55:14 -0400	[thread overview]
Message-ID: <20211029215516.801593-4-cmirabil@redhat.com> (raw)
In-Reply-To: <20211029215516.801593-1-cmirabil@redhat.com>

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/auxdisplay/Kconfig            |   7 +
 drivers/auxdisplay/Makefile           |   1 +
 drivers/auxdisplay/sensehat-display.c | 258 ++++++++++++++++++++++++++
 3 files changed, 266 insertions(+)
 create mode 100644 drivers/auxdisplay/sensehat-display.c

diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
index 1509cb74705a..24f5d68cfbff 100644
--- a/drivers/auxdisplay/Kconfig
+++ b/drivers/auxdisplay/Kconfig
@@ -193,6 +193,13 @@ config ARM_CHARLCD
 	  line and the Linux version on the second line, but that's
 	  still useful.
 
+config SENSEHAT_DISPLAY
+	tristate "Raspberry pi Sense HAT display driver"
+	select MFD_SENSEHAT_CORE
+	help
+	 This is a driver for the Raspberry Pi Sensehat 8x8 RBG-LED matrix
+	 you can access it as a misc device at /dev/sense-hat
+
 menuconfig PARPORT_PANEL
 	tristate "Parallel port LCD/Keypad Panel support"
 	depends on PARPORT
diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile
index 307771027c89..7e9aa479586a 100644
--- a/drivers/auxdisplay/Makefile
+++ b/drivers/auxdisplay/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_HD44780)		+= hd44780.o
 obj-$(CONFIG_HT16K33)		+= ht16k33.o
 obj-$(CONFIG_PARPORT_PANEL)	+= panel.o
 obj-$(CONFIG_LCD2S)		+= lcd2s.o
+obj-$(CONFIG_SENSEHAT_DISPLAY)	+= sensehat-display.o
diff --git a/drivers/auxdisplay/sensehat-display.c b/drivers/auxdisplay/sensehat-display.c
new file mode 100644
index 000000000000..5980ad23fd4f
--- /dev/null
+++ b/drivers/auxdisplay/sensehat-display.c
@@ -0,0 +1,257 @@
+// 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/regmap.h>
+
+#include "sensehat.h"
+
+#define GAMMA_SIZE sizeof_field(struct sensehat_display, gamma)
+#define VMEM_SIZE sizeof_field(struct sensehat_display, vmem)
+
+static int sensehat_update_display(struct sensehat *sensehat);
+
+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 sensehat_display_fops;
+
+static int sensehat_display_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	struct sensehat *sensehat = dev_get_drvdata(&pdev->dev);
+	struct sensehat_display *sensehat_display = &sensehat->display;
+
+	memcpy(sensehat_display->gamma, gamma_presets[lowlight], GAMMA_SIZE);
+
+	memset(sensehat_display->vmem, 0, VMEM_SIZE);
+
+	mutex_init(&sensehat_display->rw_mtx);
+
+	sensehat_display->mdev = (struct miscdevice) {
+		.minor	= MISC_DYNAMIC_MINOR,
+		.name	= "sense-hat",
+		.mode	= 0666,
+		.fops	= &sensehat_display_fops,
+	};
+
+	ret = misc_register(&sensehat_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",
+		sensehat_display->mdev.minor);
+
+	sensehat_update_display(sensehat);
+	return 0;
+}
+
+static int sensehat_display_remove(struct platform_device *pdev)
+{
+	struct sensehat *sensehat = dev_get_drvdata(&pdev->dev);
+	struct sensehat_display *sensehat_display = &sensehat->display;
+
+	misc_deregister(&sensehat_display->mdev);
+	return 0;
+}
+
+static loff_t sensehat_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
+sensehat_display_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
+{
+	struct sensehat *sensehat = container_of(filp->private_data, struct sensehat, display.mdev);
+	struct sensehat_display *sensehat_display = &sensehat->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(&sensehat_display->rw_mtx))
+		return -ERESTARTSYS;
+	if (copy_to_user(buf, sensehat_display->vmem + *f_pos, count))
+		goto out;
+	*f_pos += count;
+	retval = count;
+out:
+	mutex_unlock(&sensehat_display->rw_mtx);
+	return retval;
+}
+
+static ssize_t
+sensehat_display_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
+{
+	struct sensehat *sensehat = container_of(filp->private_data, struct sensehat, display.mdev);
+	struct sensehat_display *sensehat_display = &sensehat->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(&sensehat_display->rw_mtx))
+		return -ERESTARTSYS;
+	memcpy(sensehat_display->vmem + *f_pos, temp, count);
+	sensehat_update_display(sensehat);
+	*f_pos += count;
+	mutex_unlock(&sensehat_display->rw_mtx);
+	return count;
+}
+
+static long sensehat_display_ioctl(struct file *filp, unsigned int cmd,
+			     unsigned long arg)
+{
+	struct sensehat *sensehat = container_of(filp->private_data, struct sensehat, display.mdev);
+	struct sensehat_display *sensehat_display = &sensehat->display;
+	void __user *user_ptr = (void __user *)arg;
+	u8 temp[GAMMA_SIZE];
+	int ret;
+
+	if (mutex_lock_interruptible(&sensehat_display->rw_mtx))
+		return -ERESTARTSYS;
+	switch (cmd) {
+	case SENSEDISP_IOGET_GAMMA:
+		if (copy_to_user(user_ptr, sensehat_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(sensehat_display->gamma, temp, GAMMA_SIZE);
+	sensehat_update_display(sensehat);
+out_unlock:
+	mutex_unlock(&sensehat_display->rw_mtx);
+	return ret;
+}
+
+int sensehat_update_display(struct sensehat *sensehat)
+{
+	int i, j, ret;
+	struct sensehat_display *display = &sensehat->display;
+	sensehat_fb_t pixel_data;
+
+	for (i = 0; i < 8; ++i) {
+		for (j = 0; j < 8; ++j) {
+			pixel_data[i][0][j] = display->gamma[display->vmem[i][j].r];
+			pixel_data[i][1][j] = display->gamma[display->vmem[i][j].g];
+			pixel_data[i][2][j] = display->gamma[display->vmem[i][j].b];
+		}
+	}
+
+	ret = regmap_bulk_write(sensehat->regmap, SENSEHAT_DISPLAY,
+				pixel_data, sizeof(pixel_data));
+	if (ret < 0)
+		dev_err(sensehat->dev, "Update to 8x8 LED matrix display failed");
+	return ret;
+}
+
+static const struct file_operations sensehat_display_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= sensehat_display_llseek,
+	.read		= sensehat_display_read,
+	.write		= sensehat_display_write,
+	.unlocked_ioctl	= sensehat_display_ioctl,
+};
+
+static struct platform_device_id sensehat_display_device_id[] = {
+	{ .name = "sensehat-display" },
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, sensehat_display_device_id);
+
+static struct platform_driver sensehat_display_driver = {
+	.probe = sensehat_display_probe,
+	.remove = sensehat_display_remove,
+	.driver = {
+		.name = "sensehat-display",
+	},
+};
+
+module_platform_driver(sensehat_display_driver);
+
+MODULE_DESCRIPTION("Raspberry Pi Sense HAT 8x8 LED matrix display driver");
+MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
+MODULE_LICENSE("GPL");
-- 
2.31.1


  parent reply	other threads:[~2021-10-29 21:54 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-10-29 21:55 [PATCH 0/5] Raspberry Pi Sense HAT driver Charles Mirabile
2021-10-29 21:55 ` [PATCH 1/5] drivers/mfd: sensehat: Raspberry Pi Sense HAT core driver Charles Mirabile
2021-10-30  8:02   ` kernel test robot
2021-10-30  8:02     ` kernel test robot
2021-10-30 17:20   ` Thomas Weißschuh
2021-11-03 16:33   ` Matthias Brugger
2021-11-03 16:53     ` nsaenzju
2021-10-29 21:55 ` [PATCH 2/5] drivers/input/joystick: sensehat: Raspberry Pi Sense HAT joystick driver Charles Mirabile
2021-10-29 22:03   ` Randy Dunlap
2021-10-30 14:02   ` kernel test robot
2021-10-30 14:02     ` kernel test robot
2021-10-29 21:55 ` Charles Mirabile [this message]
2021-10-30 22:12   ` [PATCH 3/5] drivers/auxdisplay: senshat: Raspberry Pi Sense HAT display driver kernel test robot
2021-10-30 22:12     ` kernel test robot
2021-11-03 16:47   ` Matthias Brugger
2021-10-29 21:55 ` [PATCH 4/5] Documentation: bindings/mfd: sensehat: Raspberry Pi Sense HAT device tree binding Charles Mirabile
2021-11-02 12:51   ` Rob Herring
2021-10-29 21:55 ` [PATCH 5/5] MAINTAINERS: Add sensehat driver authors to MAINTAINERS Charles Mirabile
2021-10-31 16:38 ` [PATCH 0/5] Raspberry Pi Sense HAT driver Miguel Ojeda
2021-11-01 14:18   ` Joel Savitz
2021-11-20 10:55 ` 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=20211029215516.801593-4-cmirabil@redhat.com \
    --to=cmirabil@redhat.com \
    --cc=fedora-rpi@googlegroups.com \
    --cc=jsavitz@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-rpi-kernel@lists.infradead.org \
    --cc=mguma@redhat.com \
    --cc=nsaenzju@redhat.com \
    --cc=ojeda@kernel.org \
    --cc=serge@raspberrypi.org \
    --cc=stefan.wahren@i2se.com \
    /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.