277 lines
7.2 KiB
Diff
277 lines
7.2 KiB
Diff
From 3686bcadaf45fc312a131435652c75987a4745ab Mon Sep 17 00:00:00 2001
|
|
From: Alan <Alan>
|
|
Date: Sat, 20 May 2023 17:26:21 +0800
|
|
Subject: [PATCH] Add: ws2812 RGB driver for allwinner H616
|
|
|
|
---
|
|
drivers/leds/rgb/Kconfig | 7 +
|
|
drivers/leds/rgb/Makefile | 1 +
|
|
drivers/leds/rgb/leds-ws2812.c | 230 +++++++++++++++++++++++++++++++++
|
|
3 files changed, 238 insertions(+)
|
|
create mode 100644 drivers/leds/rgb/leds-ws2812.c
|
|
|
|
diff --git a/drivers/leds/rgb/Kconfig b/drivers/leds/rgb/Kconfig
|
|
index 360c8679c6e2..3b5297d841bf 100644
|
|
--- a/drivers/leds/rgb/Kconfig
|
|
+++ b/drivers/leds/rgb/Kconfig
|
|
@@ -40,3 +40,10 @@ config LEDS_MT6370_RGB
|
|
will be called "leds-mt6370-rgb".
|
|
|
|
endif # LEDS_CLASS_MULTICOLOR
|
|
+
|
|
+config LEDS_WS2812
|
|
+ tristate "WS2812 RGB support for allwinner H616"
|
|
+ depends on PINCTRL_SUN50I_H616
|
|
+
|
|
+ help
|
|
+ Say Y here if you want to use the WS2812.
|
|
\ No newline at end of file
|
|
diff --git a/drivers/leds/rgb/Makefile b/drivers/leds/rgb/Makefile
|
|
index 8c01daf63f61..1edcb4942edf 100644
|
|
--- a/drivers/leds/rgb/Makefile
|
|
+++ b/drivers/leds/rgb/Makefile
|
|
@@ -3,3 +3,4 @@
|
|
obj-$(CONFIG_LEDS_PWM_MULTICOLOR) += leds-pwm-multicolor.o
|
|
obj-$(CONFIG_LEDS_QCOM_LPG) += leds-qcom-lpg.o
|
|
obj-$(CONFIG_LEDS_MT6370_RGB) += leds-mt6370-rgb.o
|
|
+obj-$(CONFIG_LEDS_WS2812) += leds-ws2812.o
|
|
diff --git a/drivers/leds/rgb/leds-ws2812.c b/drivers/leds/rgb/leds-ws2812.c
|
|
new file mode 100644
|
|
index 000000000000..a89030fe815e
|
|
--- /dev/null
|
|
+++ b/drivers/leds/rgb/leds-ws2812.c
|
|
@@ -0,0 +1,230 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-only
|
|
+/*
|
|
+ * Copyright (c) 2023, The Linux Foundation. All rights reserved.
|
|
+ */
|
|
+#include <linux/init.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/cdev.h>
|
|
+#include <linux/fs.h>
|
|
+#include <linux/device.h>
|
|
+#include <asm/uaccess.h>
|
|
+#include <linux/moduleparam.h>
|
|
+#include <linux/ioctl.h>
|
|
+#include <linux/version.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/rbtree.h>
|
|
+#include <linux/ktime.h>
|
|
+#include <linux/string.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <linux/time.h>
|
|
+#include <linux/hrtimer.h>
|
|
+#include <linux/pci.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/input.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/irq.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/of_gpio.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/miscdevice.h>
|
|
+#include <linux/uaccess.h>
|
|
+
|
|
+#define GPIO_BASE 0x0300B000
|
|
+#define GPIO_DAT_OFFSET(n) ((n)*0x0024 + 0x10)
|
|
+
|
|
+static uint32_t ws2812_pin = 0;
|
|
+static volatile uint32_t *ws2812_gpio_port;
|
|
+static volatile uint32_t ws2812_gpio_bit;
|
|
+static volatile uint32_t ws2812_set_val = 0;
|
|
+static volatile uint32_t ws2812_reset_val = 0;
|
|
+
|
|
+DEFINE_SPINLOCK(lock);
|
|
+
|
|
+// ws2812 reset
|
|
+static void ws2812_rst(void)
|
|
+{
|
|
+ *ws2812_gpio_port &= ~ws2812_gpio_bit;
|
|
+ udelay(200);// RES low voltage time, Above 50µs
|
|
+}
|
|
+
|
|
+static void ws2812_Write_24Bits(uint32_t grb)
|
|
+{
|
|
+ uint8_t i;
|
|
+ for (i = 0; i < 24; i++)
|
|
+ {
|
|
+ if (grb & 0x800000)
|
|
+ {
|
|
+ // loop for delay about 700ns
|
|
+ *ws2812_gpio_port = ws2812_set_val;
|
|
+ *ws2812_gpio_port = ws2812_set_val;
|
|
+ *ws2812_gpio_port = ws2812_set_val;
|
|
+ *ws2812_gpio_port = ws2812_set_val;
|
|
+ *ws2812_gpio_port = ws2812_set_val;
|
|
+ *ws2812_gpio_port = ws2812_set_val;
|
|
+ *ws2812_gpio_port = ws2812_set_val;
|
|
+ // loop for delay about 600ns
|
|
+ *ws2812_gpio_port = ws2812_reset_val;
|
|
+ *ws2812_gpio_port = ws2812_reset_val;
|
|
+ *ws2812_gpio_port = ws2812_reset_val;
|
|
+ *ws2812_gpio_port = ws2812_reset_val;
|
|
+ *ws2812_gpio_port = ws2812_reset_val;
|
|
+ *ws2812_gpio_port = ws2812_reset_val;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // loop for delay about 200ns
|
|
+ *ws2812_gpio_port = ws2812_set_val;
|
|
+ *ws2812_gpio_port = ws2812_set_val;
|
|
+ // loop for delay about 800ns
|
|
+ *ws2812_gpio_port = ws2812_reset_val;
|
|
+ *ws2812_gpio_port = ws2812_reset_val;
|
|
+ *ws2812_gpio_port = ws2812_reset_val;
|
|
+ *ws2812_gpio_port = ws2812_reset_val;
|
|
+ *ws2812_gpio_port = ws2812_reset_val;
|
|
+ *ws2812_gpio_port = ws2812_reset_val;
|
|
+ *ws2812_gpio_port = ws2812_reset_val;
|
|
+ *ws2812_gpio_port = ws2812_reset_val;
|
|
+ }
|
|
+ grb <<= 1;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void ws2812_write_array(uint32_t *rgb, uint32_t cnt)
|
|
+{
|
|
+ uint32_t i = 0;
|
|
+ unsigned long flags;
|
|
+
|
|
+ for (i = 0; i < cnt; i++)
|
|
+ {
|
|
+ // rgb -> grb
|
|
+ rgb[i] = (((rgb[i] >> 16) & 0xff) << 8) | (((rgb[i] >> 8) & 0xff) << 16) | ((rgb[i]) & 0xff);
|
|
+ }
|
|
+
|
|
+ spin_lock_irqsave(&lock, flags);
|
|
+ ws2812_set_val = *ws2812_gpio_port | ws2812_gpio_bit;
|
|
+ ws2812_reset_val = *ws2812_gpio_port & (~ws2812_gpio_bit);
|
|
+ ws2812_rst();
|
|
+ for (i = 0; i < cnt; i++)
|
|
+ {
|
|
+ ws2812_Write_24Bits(rgb[i]);
|
|
+ }
|
|
+ spin_unlock_irqrestore(&lock, flags);
|
|
+}
|
|
+
|
|
+ssize_t ws2812_read(struct file *file, char __user *user, size_t bytesize, loff_t *this_loff_t)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+ssize_t ws2812_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
|
|
+{
|
|
+ uint32_t rgb[255];
|
|
+ unsigned long ret = 0;
|
|
+
|
|
+ if (count > 255 * 4) count = 255 * 4;
|
|
+ ret = copy_from_user(&rgb[0], user_buf, count);
|
|
+ if (ret < 0)
|
|
+ {
|
|
+ printk("copy_from_user fail!!!\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ ws2812_write_array((uint32_t *)rgb, count / 4);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int ws2812_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int ws2812_close(struct inode *inode, struct file *file)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct file_operations ws2812_ops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .open = ws2812_open,
|
|
+ .release = ws2812_close,
|
|
+ .write = ws2812_write,
|
|
+};
|
|
+
|
|
+static struct miscdevice ws2812_misc_dev = {
|
|
+ .minor = MISC_DYNAMIC_MINOR,
|
|
+ .name = "ws2812-led",
|
|
+ .fops = &ws2812_ops,
|
|
+};
|
|
+
|
|
+static int ws2812_probe(struct platform_device *pdev)
|
|
+{
|
|
+ int ret;
|
|
+ enum of_gpio_flags flag;
|
|
+ struct device_node *ws2812_gpio_node = pdev->dev.of_node;
|
|
+ uint32_t rgb_cnt = 0;
|
|
+ uint32_t rgb[255];
|
|
+
|
|
+ of_property_read_u32(ws2812_gpio_node, "rgb_cnt", &rgb_cnt);
|
|
+ if (rgb_cnt > 255)
|
|
+ rgb_cnt = 255;
|
|
+
|
|
+ of_property_read_u32_array(ws2812_gpio_node, "rgb_value", rgb, rgb_cnt);
|
|
+ ws2812_pin = of_get_named_gpio_flags(ws2812_gpio_node, "gpios", 0, &flag);
|
|
+ if (!gpio_is_valid(ws2812_pin))
|
|
+ {
|
|
+ printk(KERN_ERR "ws2812: gpio: %d is invalid\n", ws2812_pin);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ ws2812_gpio_port = ioremap(GPIO_BASE + GPIO_DAT_OFFSET((ws2812_pin >> 5)), 4);
|
|
+ ws2812_gpio_bit = 1 << (ws2812_pin & 0x001F);
|
|
+
|
|
+ if (gpio_request(ws2812_pin, "ws2812-gpio"))
|
|
+ {
|
|
+ printk(KERN_ERR "ws2812: gpio %d request failed!\n", ws2812_pin);
|
|
+ gpio_free(ws2812_pin);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+ gpio_direction_output(ws2812_pin, 0);
|
|
+
|
|
+ ret = misc_register(&ws2812_misc_dev);
|
|
+ msleep(50);
|
|
+
|
|
+ ws2812_write_array(rgb, rgb_cnt);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ws2812_remove(struct platform_device *pdev)
|
|
+{
|
|
+ misc_deregister(&ws2812_misc_dev);
|
|
+ gpio_free(ws2812_pin);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct of_device_id ws2812_of_match[] = {
|
|
+ {.compatible = "rgb-ws2812"},
|
|
+ {/* sentinel */}};
|
|
+
|
|
+MODULE_DEVICE_TABLE(of, ws2812_of_match);
|
|
+
|
|
+static struct platform_driver ws2812_driver = {
|
|
+ .probe = ws2812_probe,
|
|
+ .remove = ws2812_remove,
|
|
+ .driver = {
|
|
+ .name = "ws2812_ctl",
|
|
+ .of_match_table = ws2812_of_match,
|
|
+ },
|
|
+};
|
|
+
|
|
+module_platform_driver(ws2812_driver);
|
|
+
|
|
+MODULE_AUTHOR("MacLodge, Alan Ma <tech@biqu3d.com>");
|
|
+MODULE_DESCRIPTION("WS2812 RGB driver for Allwinner");
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_ALIAS("platform:ws2812_ctl");
|
|
--
|
|
2.34.1
|
|
|