From 3686bcadaf45fc312a131435652c75987a4745ab Mon Sep 17 00:00:00 2001 From: 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("WS2812 RGB driver for Allwinner"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ws2812_ctl"); -- 2.34.1