build/patch/kernel/archive/sunxi-6.2/patches.armbian/drv-rgb-add-ws2812.patch

277 lines
7.2 KiB
Diff

From 40b7424975e8868079003fda0824b7febbc7acbc Mon Sep 17 00:00:00 2001
From: Alan <Alan>
Date: Sat, 20 May 2023 17:26:21 +0800
Subject: [PATCH 11/13] 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 204cf470beae..b3f78f0775ea 100644
--- a/drivers/leds/rgb/Kconfig
+++ b/drivers/leds/rgb/Kconfig
@@ -27,3 +27,10 @@ config LEDS_QCOM_LPG
If compiled as a module, the module will be named leds-qcom-lpg.
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 0675bc0f6e18..16bcdbd71150 100644
--- a/drivers/leds/rgb/Makefile
+++ b/drivers/leds/rgb/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_LEDS_PWM_MULTICOLOR) += leds-pwm-multicolor.o
obj-$(CONFIG_LEDS_QCOM_LPG) += leds-qcom-lpg.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