D1s DMA驱动Ledc 问题
-
各位大佬有谁用过D1S的Ledc驱动器啊,我用的是Meils4
,在测试Ledc驱动器的时候,这个ledc有两种模式,一个是CPU,一个是DMA灯用的是ws2812
CPU驱动模式一下,Led可以亮但是,那个刷新LED数据比较快的情况下LED头尾的灯颜色就不正常了,最短间隔150ms,这个速度想要做点多灯的驱动肯定不行
于是修改了led的配置参数,我把那个wait data那个时间修改后,就可以刷更快了,但是具体那个时许参数是什么作用呢?然后还有个问题那个Ledc换了DMA模式以后就直接全黑了,没输出了,这又是为啥啊,没有DMA,全靠cpu的话没法用啊……
-
// SPDX-License-Identifier: GPL-2.0-only /* * drivers/leds/leds-sunxi.c - Allwinner RGB LED Driver * * Copyright (C) 2018 Allwinner Technology Limited. All rights reserved. * http://www.allwinnertech.com * *Author : Albert Yu <yuxyun@allwinnertech.com> * Lewis <liuyu@allwinnertech.com> * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */ #include <linux/module.h> #include <linux/delay.h> #include <linux/leds.h> #include <linux/io.h> #include <linux/of.h> #include <linux/slab.h> #include <linux/clk.h> #include <linux/dmaengine.h> #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/pinctrl/consumer.h> #include <linux/dma-mapping.h> #include <linux/debugfs.h> #include <linux/uaccess.h> #include <linux/delay.h> #include <linux/regulator/consumer.h> #include <linux/reset.h> #if IS_ENABLED(CONFIG_PM) #include <linux/pm.h> #endif #include "leds-sunxi.h" /* For debug */ #define LED_ERR(fmt, arg...) pr_err("%s()%d - "fmt, __func__, __LINE__, ##arg) #define dprintk(level_mask, fmt, arg...) \ do { \ if (unlikely(debug_mask & level_mask)) \ pr_warn("%s()%d - "fmt, __func__, __LINE__, ##arg); \ } while (0) static u32 debug_mask = 1; static struct sunxi_led *sunxi_led_global; static struct class *led_class; #define sunxi_slave_id(d, s) (((d)<<16) | (s)) /*For Driver */ static void led_dump_reg(struct sunxi_led *led, u32 offset, u32 len) { u32 i; u8 buf[64], cnt = 0; for (i = 0; i < len; i = i + REG_INTERVAL) { if (i%HEXADECIMAL == 0) cnt += sprintf(buf + cnt, "0x%08x: ", (u32)(led->res->start + offset + i)); cnt += sprintf(buf + cnt, "%08x ", readl(led->iomem_reg_base + offset + i)); if (i%HEXADECIMAL == REG_CL) { pr_warn("%s\n", buf); cnt = 0; } } } static void sunxi_clk_get(struct sunxi_led *led) { struct device *dev = led->dev; struct device_node *np = dev->of_node; led->clk_ledc = of_clk_get(np, 0); if (IS_ERR(led->clk_ledc)) LED_ERR("failed to get clk_ledc!\n"); led->clk_cpuapb = of_clk_get(np, 1); if (IS_ERR(led->clk_cpuapb)) LED_ERR("failed to get clk_cpuapb!\n"); } static void sunxi_clk_put(struct sunxi_led *led) { clk_put(led->clk_ledc); clk_put(led->clk_cpuapb); led->clk_ledc = NULL; led->clk_cpuapb = NULL; } static void sunxi_clk_enable(struct sunxi_led *led) { clk_prepare_enable(led->clk_ledc); clk_prepare_enable(led->clk_cpuapb); } static void sunxi_clk_disable(struct sunxi_led *led) { clk_disable_unprepare(led->clk_ledc); } static void sunxi_clk_init(struct sunxi_led *led) { sunxi_clk_get(led); sunxi_clk_enable(led); } static void sunxi_clk_deinit(struct sunxi_led *led) { sunxi_clk_disable(led); sunxi_clk_put(led); } static u32 sunxi_get_reg(int offset) { struct sunxi_led *led = sunxi_led_global; u32 value = ioread32(((u8 *)led->iomem_reg_base) + offset); return value; } static void sunxi_set_reg(int offset, u32 value) { struct sunxi_led *led = sunxi_led_global; iowrite32(value, ((u8 *)led->iomem_reg_base) + offset); } static inline void sunxi_set_reset_ns(struct sunxi_led *led) { u32 n, reg_val; u32 mask = 0x1FFF; u32 min = SUNXI_RESET_TIME_MIN_NS; u32 max = SUNXI_RESET_TIME_MAX_NS; if (led->reset_ns < min || led->reset_ns > max) { LED_ERR("invalid parameter, reset_ns should be %u-%u!\n", min, max); return; } n = (led->reset_ns - 42) / 42; reg_val = sunxi_get_reg(LED_RESET_TIMING_CTRL_REG_OFFSET); reg_val &= ~(mask << 16); reg_val |= (n << 16); sunxi_set_reg(LED_RESET_TIMING_CTRL_REG_OFFSET, reg_val); } static inline void sunxi_set_t1h_ns(struct sunxi_led *led) { u32 n, reg_val; u32 mask = 0x3F; u32 shift = 21; u32 min = SUNXI_T1H_MIN_NS; u32 max = SUNXI_T1H_MAX_NS; if (led->t1h_ns < min || led->t1h_ns > max) { LED_ERR("invalid parameter, t1h_ns should be %u-%u!\n", min, max); return; } n = (led->t1h_ns - 42) / 42; reg_val = sunxi_get_reg(LED_T01_TIMING_CTRL_REG_OFFSET); reg_val &= ~(mask << shift); reg_val |= n << shift; sunxi_set_reg(LED_T01_TIMING_CTRL_REG_OFFSET, reg_val); } static inline void sunxi_set_t1l_ns(struct sunxi_led *led) { u32 n, reg_val; u32 mask = 0x1F; u32 shift = 16; u32 min = SUNXI_T1L_MIN_NS; u32 max = SUNXI_T1L_MAX_NS; if (led->t1l_ns < min || led->t1l_ns > max) { LED_ERR("invalid parameter, t1l_ns should be %u-%u!\n", min, max); return; } n = (led->t1l_ns - 42) / 42; reg_val = sunxi_get_reg(LED_T01_TIMING_CTRL_REG_OFFSET); reg_val &= ~(mask << shift); reg_val |= n << shift; sunxi_set_reg(LED_T01_TIMING_CTRL_REG_OFFSET, reg_val); } static inline void sunxi_set_t0h_ns(struct sunxi_led *led) { u32 n, reg_val; u32 mask = 0x1F; u32 shift = 6; u32 min = SUNXI_T0H_MIN_NS; u32 max = SUNXI_T0H_MAX_NS; if (led->t0h_ns < min || led->t0h_ns > max) { LED_ERR("invalid parameter, t0h_ns should be %u-%u!\n", min, max); return; } n = (led->t0h_ns - 42) / 42; reg_val = sunxi_get_reg(LED_T01_TIMING_CTRL_REG_OFFSET); reg_val &= ~(mask << shift); reg_val |= n << shift; sunxi_set_reg(LED_T01_TIMING_CTRL_REG_OFFSET, reg_val); } static inline void sunxi_set_t0l_ns(struct sunxi_led *led) { u32 n, reg_val; u32 min = SUNXI_T0L_MIN_NS; u32 max = SUNXI_T0L_MAX_NS; if (led->t0l_ns < min || led->t0l_ns > max) { LED_ERR("invalid parameter, t0l_ns should be %u-%u!\n", min, max); return; } n = (led->t0l_ns - 42) / 42; reg_val = sunxi_get_reg(LED_T01_TIMING_CTRL_REG_OFFSET); reg_val &= ~0x3F; reg_val |= n; sunxi_set_reg(LED_T01_TIMING_CTRL_REG_OFFSET, reg_val); } static inline void sunxi_set_wait_time0_ns(struct sunxi_led *led) { u32 n, reg_val; u32 min = SUNXI_WAIT_TIME0_MIN_NS; u32 max = SUNXI_WAIT_TIME0_MAX_NS; if (led->wait_time0_ns < min || led->wait_time0_ns > max) { LED_ERR("invalid parameter, wait_time0_ns should be %u-%u!\n", min, max); return; } n = (led->wait_time0_ns - 42) / 42; reg_val = (1 << 8) | n; sunxi_set_reg(LEDC_WAIT_TIME0_CTRL_REG, reg_val); } static inline void sunxi_set_wait_time1_ns(struct sunxi_led *led) { unsigned long long tmp, max = SUNXI_WAIT_TIME1_MAX_NS; u32 min = SUNXI_WAIT_TIME1_MIN_NS; u32 n, reg_val; if (led->wait_time1_ns < min || led->wait_time1_ns > max) { LED_ERR("invalid parameter, wait_time1_ns should be %u-%llu!\n", min, max); return; } tmp = led->wait_time1_ns; n = div_u64(tmp, 42); n -= 1; reg_val = (1 << 31) | n; sunxi_set_reg(LEDC_WAIT_TIME1_CTRL_REG, reg_val); } static inline void sunxi_set_wait_data_time_ns(struct sunxi_led *led) { u32 min, max; #ifndef SUNXI_FPGA_LEDC u32 mask = 0x1FFF, shift = 16, reg_val = 0, n; #endif min = SUNXI_WAIT_DATA_TIME_MIN_NS; #ifdef SUNXI_FPGA_LEDC /* * For FPGA platforms, it is easy to meet wait data timeout for * the obvious latency of task which is because of less cpu cores * and lower cpu frequency compared with IC platforms, so here we * permit long enough time latency. */ max = SUNXI_WAIT_DATA_TIME_MAX_NS_FPGA; #else /* SUNXI_FPGA_LEDC */ max = SUNXI_WAIT_DATA_TIME_MAX_NS_IC; #endif /* SUNXI_FPGA_LEDC */ if (led->wait_data_time_ns < min || led->wait_data_time_ns > max) { LED_ERR("invalid parameter, wait_data_time_ns should be %u-%u!\n", min, max); return; } #ifndef SUNXI_FPGA_LEDC n = (led->wait_data_time_ns - 42) / 42; reg_val &= ~(mask << shift); reg_val |= (n << shift); sunxi_set_reg(LEDC_DATA_FINISH_CNT_REG_OFFSET, reg_val); #endif /* SUNXI_FPGA_LEDC */ } static void sunxi_ledc_set_time(struct sunxi_led *led) { sunxi_set_reset_ns(led); sunxi_set_t1h_ns(led); sunxi_set_t1l_ns(led); sunxi_set_t0h_ns(led); sunxi_set_t0l_ns(led); sunxi_set_wait_time0_ns(led); sunxi_set_wait_time1_ns(led); sunxi_set_wait_data_time_ns(led); } static void sunxi_ledc_set_length(struct sunxi_led *led) { u32 reg_val; u32 length = led->length; if (length == 0) return; if (length > led->led_count) return; reg_val = sunxi_get_reg(LEDC_CTRL_REG_OFFSET); reg_val &= ~(0x1FFF << 16); reg_val |= length << 16; sunxi_set_reg(LEDC_CTRL_REG_OFFSET, reg_val); reg_val = sunxi_get_reg(LED_RESET_TIMING_CTRL_REG_OFFSET); reg_val &= ~0x3FF; reg_val |= length - 1; sunxi_set_reg(LED_RESET_TIMING_CTRL_REG_OFFSET, reg_val); } static void sunxi_ledc_set_output_mode(struct sunxi_led *led, const char *str) { u32 val; u32 mask = 0x7; u32 shift = 6; u32 reg_val = sunxi_get_reg(LEDC_CTRL_REG_OFFSET); if (str != NULL) { if (!strncmp(str, "GRB", 3)) val = SUNXI_OUTPUT_GRB; else if (!strncmp(str, "GBR", 3)) val = SUNXI_OUTPUT_GBR; else if (!strncmp(str, "RGB", 3)) val = SUNXI_OUTPUT_RGB; else if (!strncmp(str, "RBG", 3)) val = SUNXI_OUTPUT_RBG; else if (!strncmp(str, "BGR", 3)) val = SUNXI_OUTPUT_BGR; else if (!strncmp(str, "BRG", 3)) val = SUNXI_OUTPUT_BRG; else return; } else { val = led->output_mode.val; } reg_val &= ~(mask << shift); reg_val |= val; sunxi_set_reg(LEDC_CTRL_REG_OFFSET, reg_val); if (str != NULL) { if (strncmp(str, led->output_mode.str, 3)) memcpy(led->output_mode.str, str, 3); } if (val != led->output_mode.val) led->output_mode.val = val; } static void sunxi_ledc_enable_irq(u32 mask) { u32 reg_val = 0; reg_val |= mask; sunxi_set_reg(LEDC_INT_CTRL_REG_OFFSET, reg_val); } static void sunxi_ledc_disable_irq(u32 mask) { u32 reg_val = 0; reg_val = sunxi_get_reg(LEDC_INT_CTRL_REG_OFFSET); reg_val &= ~mask; sunxi_set_reg(LEDC_INT_CTRL_REG_OFFSET, reg_val); } static inline void sunxi_ledc_enable(struct sunxi_led *led) { u32 reg_val; reg_val = sunxi_get_reg(LEDC_CTRL_REG_OFFSET); reg_val |= 1; sunxi_set_reg(LEDC_CTRL_REG_OFFSET, reg_val); } static inline void sunxi_ledc_reset(struct sunxi_led *led) { u32 reg_val = sunxi_get_reg(LEDC_CTRL_REG_OFFSET); sunxi_ledc_disable_irq(LEDC_TRANS_FINISH_INT_EN | LEDC_FIFO_CPUREQ_INT_EN | LEDC_WAITDATA_TIMEOUT_INT_EN | LEDC_FIFO_OVERFLOW_INT_EN | LEDC_GLOBAL_INT_EN); if (debug_mask & DEBUG_INFO2) { dprintk(DEBUG_INFO2, "dump reg:\n"); led_dump_reg(led, 0, 0x30); } reg_val |= 1 << 1; sunxi_set_reg(LEDC_CTRL_REG_OFFSET, reg_val); } #ifdef CONFIG_DEBUG_FS static ssize_t reset_ns_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) { int err; char buffer[64]; u32 min, max; unsigned long val; struct sunxi_led *led = sunxi_led_global; min = SUNXI_RESET_TIME_MIN_NS; max = SUNXI_RESET_TIME_MAX_NS; if (count >= sizeof(buffer)) goto err_out; if (copy_from_user(buffer, buf, count)) goto err_out; buffer[count] = '\0'; err = kstrtoul(buffer, 10, &val); if (err) goto err_out; if (val < min || val > max) goto err_out; led->reset_ns = val; sunxi_set_reset_ns(led); *offp += count; return count; err_out: LED_ERR("invalid parameter, reset_ns should be %u-%u!\n", min, max); return -EINVAL; } static ssize_t reset_ns_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) { int r; char buffer[64]; struct sunxi_led *led = sunxi_led_global; r = snprintf(buffer, 64, "%u\n", led->reset_ns); return simple_read_from_buffer(buf, count, offp, buffer, r); } static const struct file_operations reset_ns_fops = { .owner = THIS_MODULE, .write = reset_ns_write, .read = reset_ns_read, }; static ssize_t t1h_ns_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) { int err; char buffer[64]; u32 min, max; unsigned long val; struct sunxi_led *led = sunxi_led_global; min = SUNXI_T1H_MIN_NS; max = SUNXI_T1H_MAX_NS; if (count >= sizeof(buffer)) return -EINVAL; if (copy_from_user(buffer, buf, count)) return -EFAULT; buffer[count] = '\0'; err = kstrtoul(buffer, 10, &val); if (err) return -EINVAL; if (val < min || val > max) goto err_out; led->t1h_ns = val; sunxi_set_t1h_ns(led); *offp += count; return count; err_out: LED_ERR("invalid parameter, t1h_ns should be %u-%u!\n", min, max); return -EINVAL; } static ssize_t t1h_ns_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) { int r; char buffer[64]; struct sunxi_led *led = sunxi_led_global; r = snprintf(buffer, 64, "%u\n", led->t1h_ns); return simple_read_from_buffer(buf, count, offp, buffer, r); } static const struct file_operations t1h_ns_fops = { .owner = THIS_MODULE, .write = t1h_ns_write, .read = t1h_ns_read, }; static ssize_t t1l_ns_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) { int err; char buffer[64]; u32 min, max; unsigned long val; struct sunxi_led *led = sunxi_led_global; min = SUNXI_T1L_MIN_NS; max = SUNXI_T1L_MAX_NS; if (count >= sizeof(buffer)) goto err_out; if (copy_from_user(buffer, buf, count)) goto err_out; buffer[count] = '\0'; err = kstrtoul(buffer, 10, &val); if (err) goto err_out; if (val < min || val > max) goto err_out; led->t1l_ns = val; sunxi_set_t1l_ns(led); *offp += count; return count; err_out: LED_ERR("invalid parameter, t1l_ns should be %u-%u!\n", min, max); return -EINVAL; } static ssize_t t1l_ns_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) { int r; char buffer[64]; struct sunxi_led *led = sunxi_led_global; r = snprintf(buffer, 64, "%u\n", led->t1l_ns); return simple_read_from_buffer(buf, count, offp, buffer, r); } static const struct file_operations t1l_ns_fops = { .owner = THIS_MODULE, .write = t1l_ns_write, .read = t1l_ns_read, }; static ssize_t t0h_ns_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) { int err; char buffer[64]; u32 min, max; unsigned long val; struct sunxi_led *led = sunxi_led_global; min = SUNXI_T0H_MIN_NS; max = SUNXI_T0H_MAX_NS; if (count >= sizeof(buffer)) goto err_out; if (copy_from_user(buffer, buf, count)) goto err_out; buffer[count] = '\0'; err = kstrtoul(buffer, 10, &val); if (err) goto err_out; if (val < min || val > max) goto err_out; led->t0h_ns = val; sunxi_set_t0h_ns(led); *offp += count; return count; err_out: LED_ERR("invalid parameter, t0h_ns should be %u-%u!\n", min, max); return -EINVAL; } static ssize_t t0h_ns_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) { int r; char buffer[64]; struct sunxi_led *led = sunxi_led_global; r = snprintf(buffer, 64, "%u\n", led->t0h_ns); return simple_read_from_buffer(buf, count, offp, buffer, r); } static const struct file_operations t0h_ns_fops = { .owner = THIS_MODULE, .write = t0h_ns_write, .read = t0h_ns_read, }; static ssize_t t0l_ns_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) { int err; char buffer[64]; u32 min, max; unsigned long val; struct sunxi_led *led = sunxi_led_global; min = SUNXI_T0L_MIN_NS; max = SUNXI_T0L_MAX_NS; if (count >= sizeof(buffer)) goto err_out; if (copy_from_user(buffer, buf, count)) goto err_out; buffer[count] = '\0'; err = kstrtoul(buffer, 10, &val); if (err) goto err_out; if (val < min || val > max) goto err_out; led->t0l_ns = val; sunxi_set_t0l_ns(led); *offp += count; return count; err_out: LED_ERR("invalid parameter, t0l_ns should be %u-%u!\n", min, max); return -EINVAL; } static ssize_t t0l_ns_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) { int r; char buffer[64]; struct sunxi_led *led = sunxi_led_global; r = snprintf(buffer, 64, "%u\n", led->t0l_ns); return simple_read_from_buffer(buf, count, offp, buffer, r); } static const struct file_operations t0l_ns_fops = { .owner = THIS_MODULE, .write = t0l_ns_write, .read = t0l_ns_read, }; static ssize_t wait_time0_ns_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) { int err; char buffer[64]; u32 min, max; unsigned long val; struct sunxi_led *led = sunxi_led_global; min = SUNXI_WAIT_TIME0_MIN_NS; max = SUNXI_WAIT_TIME0_MAX_NS; if (count >= sizeof(buffer)) goto err_out; if (copy_from_user(buffer, buf, count)) goto err_out; buffer[count] = '\0'; err = kstrtoul(buffer, 10, &val); if (err) goto err_out; if (val < min || val > max) goto err_out; led->wait_time0_ns = val; sunxi_set_wait_time0_ns(led); *offp += count; return count; err_out: LED_ERR("invalid parameter, wait_time0_ns should be %u-%u!\n", min, max); return -EINVAL; } static ssize_t wait_time0_ns_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) { int r; char buffer[64]; struct sunxi_led *led = sunxi_led_global; r = snprintf(buffer, 64, "%u\n", led->wait_time0_ns); return simple_read_from_buffer(buf, count, offp, buffer, r); } static const struct file_operations wait_time0_ns_fops = { .owner = THIS_MODULE, .write = wait_time0_ns_write, .read = wait_time0_ns_read, }; static ssize_t wait_time1_ns_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) { int err; char buffer[64]; u32 min; unsigned long long max; unsigned long long val; struct sunxi_led *led = sunxi_led_global; min = SUNXI_WAIT_TIME1_MIN_NS; max = SUNXI_WAIT_TIME1_MAX_NS; if (count >= sizeof(buffer)) goto err_out; if (copy_from_user(buffer, buf, count)) goto err_out; buffer[count] = '\0'; err = kstrtoull(buffer, 10, &val); if (err) goto err_out; if (val < min || val > max) goto err_out; led->wait_time1_ns = val; sunxi_set_wait_time1_ns(led); *offp += count; return count; err_out: LED_ERR("invalid parameter, wait_time1_ns should be %u-%lld!\n", min, max); return -EINVAL; } static ssize_t wait_time1_ns_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) { int r; char buffer[64]; struct sunxi_led *led = sunxi_led_global; r = snprintf(buffer, 64, "%lld\n", led->wait_time1_ns); return simple_read_from_buffer(buf, count, offp, buffer, r); } static const struct file_operations wait_time1_ns_fops = { .owner = THIS_MODULE, .write = wait_time1_ns_write, .read = wait_time1_ns_read, }; static ssize_t wait_data_time_ns_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) { int err; char buffer[64]; u32 min, max; unsigned long val; struct sunxi_led *led = sunxi_led_global; min = SUNXI_WAIT_DATA_TIME_MIN_NS; #ifdef SUNXI_FPGA_LEDC max = SUNXI_WAIT_DATA_TIME_MAX_NS_FPGA; #else max = SUNXI_WAIT_DATA_TIME_MAX_NS_IC; #endif if (count >= sizeof(buffer)) goto err_out; if (copy_from_user(buffer, buf, count)) goto err_out; buffer[count] = '\0'; err = kstrtoul(buffer, 10, &val); if (err) goto err_out; if (val < min || val > max) goto err_out; led->wait_data_time_ns = val; sunxi_set_wait_data_time_ns(led); *offp += count; return count; err_out: LED_ERR("invalid parameter, wait_data_time_ns should be %u-%u!\n", min, max); return -EINVAL; } static ssize_t wait_data_time_ns_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) { int r; char buffer[64]; struct sunxi_led *led = sunxi_led_global; r = snprintf(buffer, 64, "%u\n", led->wait_data_time_ns); return simple_read_from_buffer(buf, count, offp, buffer, r); } static const struct file_operations wait_data_time_ns_fops = { .owner = THIS_MODULE, .write = wait_data_time_ns_write, .read = wait_data_time_ns_read, }; static int data_show(struct seq_file *s, void *data) { int i; struct sunxi_led *led = sunxi_led_global; for (i = 0; i < led->led_count; i++) { if (!(i % 4)) { if (i + 4 <= led->led_count) seq_printf(s, "%04d-%04d", i, i + 4); else seq_printf(s, "%04d-%04d", i, led->led_count); } seq_printf(s, " 0x%08x", led->data[i]); if (((i % 4) == 3) || (i == led->led_count - 1)) seq_puts(s, "\n"); } return 0; } static int data_open(struct inode *inode, struct file *file) { return single_open(file, data_show, inode->i_private); } static const struct file_operations data_fops = { .owner = THIS_MODULE, .open = data_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static ssize_t output_mode_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) { char buffer[64]; struct sunxi_led *led = sunxi_led_global; if (count >= sizeof(buffer)) goto err_out; if (copy_from_user(buffer, buf, count)) goto err_out; buffer[count] = '\0'; sunxi_ledc_set_output_mode(led, buffer); *offp += count; return count; err_out: LED_ERR("invalid parameter!\n"); return -EINVAL; } static ssize_t output_mode_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) { int r; char buffer[64]; struct sunxi_led *led = sunxi_led_global; r = snprintf(buffer, 64, "%s\n", led->output_mode.str); return simple_read_from_buffer(buf, count, offp, buffer, r); } static const struct file_operations output_mode_fops = { .owner = THIS_MODULE, .write = output_mode_write, .read = output_mode_read, }; static ssize_t hwversion_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) { int r; char buffer[64]; u32 reg_val, major_ver, minor_ver; reg_val = sunxi_get_reg(LEDC_VER_NUM_REG); major_ver = reg_val >> 16; minor_ver = reg_val & 0xF; r = snprintf(buffer, 64, "r%up%u\n", major_ver, minor_ver); return simple_read_from_buffer(buf, count, offp, buffer, r); } static const struct file_operations hwversion_fops = { .owner = THIS_MODULE, .read = hwversion_read, }; static void sunxi_led_create_debugfs(struct sunxi_led *led) { struct dentry *debugfs_dir, *debugfs_file; debugfs_dir = debugfs_create_dir("sunxi_leds", NULL); if (IS_ERR_OR_NULL(debugfs_dir)) { LED_ERR("debugfs_create_dir failed!\n"); return; } led->debugfs_dir = debugfs_dir; debugfs_file = debugfs_create_file("reset_ns", 0660, debugfs_dir, NULL, &reset_ns_fops); if (!debugfs_file) LED_ERR("debugfs_create_file for reset_ns failed!\n"); debugfs_file = debugfs_create_file("t1h_ns", 0660, debugfs_dir, NULL, &t1h_ns_fops); if (!debugfs_file) LED_ERR("debugfs_create_file for t1h_ns failed!\n"); debugfs_file = debugfs_create_file("t1l_ns", 0660, debugfs_dir, NULL, &t1l_ns_fops); if (!debugfs_file) LED_ERR("debugfs_create_file for t1l_ns failed!\n"); debugfs_file = debugfs_create_file("t0h_ns", 0660, debugfs_dir, NULL, &t0h_ns_fops); if (!debugfs_file) LED_ERR("debugfs_create_file for t0h_ns failed!\n"); debugfs_file = debugfs_create_file("t0l_ns", 0660, debugfs_dir, NULL, &t0l_ns_fops); if (!debugfs_file) LED_ERR("debugfs_create_file for t0l_ns failed!\n"); debugfs_file = debugfs_create_file("wait_time0_ns", 0660, debugfs_dir, NULL, &wait_time0_ns_fops); if (!debugfs_file) LED_ERR("debugfs_create_file for wait_time0_ns failed!\n"); debugfs_file = debugfs_create_file("wait_time1_ns", 0660, debugfs_dir, NULL, &wait_time1_ns_fops); if (!debugfs_file) LED_ERR("debugfs_create_file for wait_time1_ns failed!\n"); debugfs_file = debugfs_create_file("wait_data_time_ns", 0660, debugfs_dir, NULL, &wait_data_time_ns_fops); if (!debugfs_file) LED_ERR("debugfs_create_file for wait_data_time_ns failed!\n"); debugfs_file = debugfs_create_file("data", 0440, debugfs_dir, NULL, &data_fops); if (!debugfs_file) LED_ERR("debugfs_create_file for data failed!\n"); debugfs_file = debugfs_create_file("output_mode", 0660, debugfs_dir, NULL, &output_mode_fops); if (!debugfs_file) LED_ERR("debugfs_create_file for output_mode failed!\n"); if (!debugfs_file) LED_ERR("debugfs_create_file for trans_mode failed!\n"); debugfs_file = debugfs_create_file("hwversion", 0440, debugfs_dir, NULL, &hwversion_fops); if (!debugfs_file) LED_ERR("debugfs_create_file for hwversion failed!\n"); } static void sunxi_led_remove_debugfs(struct sunxi_led *led) { debugfs_remove_recursive(led->debugfs_dir); } #endif /* CONFIG_DEBUG_FS */ static void sunxi_ledc_set_dma_mode(struct sunxi_led *led) { u32 reg_val = 0; reg_val |= 1 << 5; sunxi_set_reg(LEDC_DMA_CTRL_REG, reg_val); sunxi_ledc_disable_irq(LEDC_FIFO_CPUREQ_INT_EN); } static void sunxi_ledc_set_cpu_mode(struct sunxi_led *led) { u32 reg_val = 0; reg_val &= ~(1 << 5); sunxi_set_reg(LEDC_DMA_CTRL_REG, reg_val); sunxi_ledc_enable_irq(LEDC_FIFO_CPUREQ_INT_EN); } static void sunxi_ledc_dma_callback(void *param) { dprintk(DEBUG_INFO, "finish\n"); } static void sunxi_ledc_trans_data(struct sunxi_led *led) { int i, err; size_t size; unsigned long flags; phys_addr_t dst_addr; struct dma_slave_config slave_config; struct device *dev = led->dev; struct dma_async_tx_descriptor *dma_desc; /* less than 32 lights use cpu transmission. */ /* more than 32 lights use dma transmission. */ if (led->length <= SUNXI_LEDC_FIFO_DEPTH) { dprintk(DEBUG_INFO, "cpu xfer\n"); ktime_get_coarse_real_ts64(&(led->start_time)); sunxi_ledc_set_time(led); sunxi_ledc_set_output_mode(led, led->output_mode.str); sunxi_ledc_set_cpu_mode(led); sunxi_ledc_set_length(led); sunxi_ledc_enable_irq(LEDC_TRANS_FINISH_INT_EN | LEDC_WAITDATA_TIMEOUT_INT_EN | LEDC_FIFO_OVERFLOW_INT_EN | LEDC_GLOBAL_INT_EN); sunxi_ledc_enable(led); for (i = 0; i < led->length; i++) sunxi_set_reg(LEDC_DATA_REG_OFFSET, led->data[i]); } else { dprintk(DEBUG_INFO, "dma xfer\n"); size = led->length * 4; led->src_dma = dma_map_single(dev, led->data, size, DMA_TO_DEVICE); dst_addr = led->res->start + LEDC_DATA_REG_OFFSET; flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; slave_config.direction = DMA_MEM_TO_DEV; slave_config.src_addr = led->src_dma; slave_config.dst_addr = dst_addr; slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; slave_config.src_maxburst = 4; slave_config.dst_maxburst = 4; err = dmaengine_slave_config(led->dma_chan, &slave_config); if (err < 0) { LED_ERR("dmaengine_slave_config failed!\n"); return; } dma_desc = dmaengine_prep_slave_single(led->dma_chan, led->src_dma, size, DMA_MEM_TO_DEV, flags); if (!dma_desc) { LED_ERR("dmaengine_prep_slave_single failed!\n"); return; } dma_desc->callback = sunxi_ledc_dma_callback; dmaengine_submit(dma_desc); dma_async_issue_pending(led->dma_chan); ktime_get_coarse_real_ts64(&(led->start_time)); sunxi_ledc_set_time(led); sunxi_ledc_set_output_mode(led, led->output_mode.str); sunxi_ledc_set_dma_mode(led); sunxi_ledc_set_length(led); sunxi_ledc_enable_irq(LEDC_TRANS_FINISH_INT_EN | LEDC_WAITDATA_TIMEOUT_INT_EN | LEDC_FIFO_OVERFLOW_INT_EN | LEDC_GLOBAL_INT_EN); sunxi_ledc_enable(led); } } static inline void sunxi_ledc_clear_all_irq(void) { u32 reg_val = sunxi_get_reg(LEDC_INT_STS_REG_OFFSET); reg_val &= ~0x1F; sunxi_set_reg(LEDC_INT_STS_REG_OFFSET, reg_val); } static inline void sunxi_ledc_clear_irq(enum sunxi_ledc_irq_status_reg irq) { u32 reg_val = sunxi_get_reg(LEDC_INT_STS_REG_OFFSET); reg_val &= ~irq; sunxi_set_reg(LEDC_INT_STS_REG_OFFSET, reg_val); } static int sunxi_ledc_complete(struct sunxi_led *led) { unsigned long flags = 0; unsigned long timeout = 0; u32 reg_val; /*wait_event_timeout return 0 : timeout *wait_event_timeout return > 0 : thr left time * */ timeout = wait_event_timeout(led->wait, led->result, 5*HZ); if (timeout == 0) { reg_val = sunxi_get_reg(LEDC_INT_STS_REG_OFFSET); pr_err("LEDC INTERRUPT STATUS REG IS %x", reg_val); LED_ERR("led xfer timeout\n"); reg_val = sunxi_get_reg(LEDC_INT_STS_REG_OFFSET); pr_err("LEDC INTERRUPT STATUS REG IS %x", reg_val); return -ETIME; } else if (led->result == RESULT_ERR) { return -ECOMM; } dprintk(DEBUG_INFO, "xfer complete\n"); spin_lock_irqsave(&led->lock, flags); led->result = 0; spin_unlock_irqrestore(&led->lock, flags); return 0; } static irqreturn_t sunxi_ledc_irq_handler(int irq, void *dev_id) { long delta_time_ns; u32 irq_status, max_ns; struct sunxi_led *led = sunxi_led_global; struct device *dev = led->dev; struct timespec64 current_time; spin_lock(&led->lock); irq_status = sunxi_get_reg(LEDC_INT_STS_REG_OFFSET); sunxi_ledc_clear_all_irq(); if (irq_status & LEDC_TRANS_FINISH_INT) { sunxi_ledc_reset(led); led->result = RESULT_COMPLETE; goto out; } if (irq_status & LEDC_WAITDATA_TIMEOUT_INT) { ktime_get_coarse_real_ts64(¤t_time); delta_time_ns = current_time.tv_sec - led->start_time.tv_sec; delta_time_ns *= 1000 * 1000 * 1000; delta_time_ns += current_time.tv_nsec - led->start_time.tv_nsec; max_ns = led->wait_data_time_ns; if (delta_time_ns <= max_ns) { spin_unlock(&led->lock); return IRQ_HANDLED; } sunxi_ledc_reset(led); if (delta_time_ns <= max_ns * 2) { sunxi_ledc_trans_data(led); } else { LED_ERR("wait time is more than %d ns," "going to reset ledc and drop this operation!\n", max_ns); led->result = RESULT_ERR; } goto out; } if (irq_status & LEDC_FIFO_OVERFLOW_INT) { LED_ERR("there exists fifo overflow issue, irq_status=0x%x!\n", irq_status); sunxi_ledc_reset(led); led->result = RESULT_ERR; goto out; } out: if (led->dma_chan) dma_unmap_single(dev, led->src_dma, led->length * 4, DMA_TO_DEVICE); wake_up(&led->wait); led->length = 0; spin_unlock(&led->lock); return IRQ_HANDLED; } static int sunxi_ledc_irq_init(struct sunxi_led *led) { int err; struct device *dev = led->dev; unsigned long flags = 0; const char *name = "ledcirq"; struct platform_device *pdev; pdev = container_of(dev, struct platform_device, dev); spin_lock_init(&led->lock); led->irqnum = platform_get_irq(pdev, 0); if (led->irqnum < 0) LED_ERR("failed to get ledc irq!\n"); err = request_irq(led->irqnum, sunxi_ledc_irq_handler, flags, name, dev); if (err) { LED_ERR("failed to install IRQ handler for irqnum %d\n", led->irqnum); return -EPERM; } return 0; } static void sunxi_ledc_irq_deinit(struct sunxi_led *led) { free_irq(led->irqnum, led->dev); sunxi_ledc_disable_irq(LEDC_TRANS_FINISH_INT_EN | LEDC_FIFO_CPUREQ_INT_EN | LEDC_WAITDATA_TIMEOUT_INT_EN | LEDC_FIFO_OVERFLOW_INT_EN | LEDC_GLOBAL_INT_EN); } static void sunxi_ledc_pinctrl_init(struct sunxi_led *led) { struct device *dev = led->dev; struct pinctrl *pinctrl = devm_pinctrl_get_select_default(dev); led->pctrl = pinctrl; if (IS_ERR(pinctrl)) LED_ERR("devm_pinctrl_get_select_default failed!\n"); } static int led_regulator_request(struct sunxi_led *led) { struct regulator *regu = NULL; /* Consider "n*" as nocare. Support "none", "nocare", "null", "" etc. */ if ((led->regulator_id[0] == 'n') || (led->regulator_id[0] == 0)) return 0; regu = regulator_get(NULL, led->regulator_id); if (IS_ERR(regu)) { LED_ERR("get regulator %s failed!\n", led->regulator_id); return -1; } led->regulator = regu; return 0; } static int led_regulator_release(struct sunxi_led *led) { if (led->regulator == NULL) return 0; regulator_put(led->regulator); led->regulator = NULL; return 1; } static int sunxi_ledc_dma_get(struct sunxi_led *led) { if (led->dma_chan == NULL) { led->dma_chan = dma_request_chan(led->dev, "tx"); if (IS_ERR(led->dma_chan)) { LED_ERR("failed to get the DMA channel!\n"); return -EFAULT; } } return 0; } static int sunxi_set_led_brightness(struct led_classdev *led_cdev, enum led_brightness value) { unsigned long flags; u32 r, g, b, shift, old_data, new_data, length; struct sunxi_led_info *pinfo; struct sunxi_led_classdev_group *pcdev_group; struct sunxi_led *led = sunxi_led_global; int err; pinfo = container_of(led_cdev, struct sunxi_led_info, cdev); switch (pinfo->type) { case LED_TYPE_G: pcdev_group = container_of(pinfo, struct sunxi_led_classdev_group, g); g = value; shift = 16; break; case LED_TYPE_R: pcdev_group = container_of(pinfo, struct sunxi_led_classdev_group, r); r = value; shift = 8; break; case LED_TYPE_B: pcdev_group = container_of(pinfo, struct sunxi_led_classdev_group, b); b = value; shift = 0; break; } old_data = led->data[pcdev_group->led_num]; if (((old_data >> shift) & 0xFF) == value) return 0; if (pinfo->type != LED_TYPE_R) r = pcdev_group->r.cdev.brightness; if (pinfo->type != LED_TYPE_G) g = pcdev_group->g.cdev.brightness; if (pinfo->type != LED_TYPE_B) b = pcdev_group->b.cdev.brightness; /* LEDC treats input data as GRB by default */ new_data = (g << 16) | (r << 8) | b; length = pcdev_group->led_num + 1; spin_lock_irqsave(&led->lock, flags); led->data[pcdev_group->led_num] = new_data; led->length = length; spin_unlock_irqrestore(&led->lock, flags); /* prepare for dma xfer, dynamic apply dma channel */ if (led->length > SUNXI_LEDC_FIFO_DEPTH) { err = sunxi_ledc_dma_get(led); if (err) return err; } sunxi_ledc_trans_data(led); if (debug_mask & DEBUG_INFO2) { dprintk(DEBUG_INFO2, "dump reg:\n"); led_dump_reg(led, 0, 0x30); } sunxi_ledc_complete(led); if (debug_mask & DEBUG_INFO1) pr_warn("num = %03u\n", length); return 0; } static int sunxi_register_led_classdev(struct sunxi_led *led) { int i, err; size_t size; struct device *dev = led->dev; struct led_classdev *pcdev_RGB; dprintk(DEBUG_INIT, "led_classdev start\n"); if (!led->led_count) led->led_count = SUNXI_DEFAULT_LED_COUNT; size = sizeof(struct sunxi_led_classdev_group) * led->led_count; led->pcdev_group = devm_kzalloc(dev, size, GFP_KERNEL); if (!led->pcdev_group) return -ENOMEM; for (i = 0; i < led->led_count; i++) { led->pcdev_group[i].r.type = LED_TYPE_R; pcdev_RGB = &led->pcdev_group[i].r.cdev; pcdev_RGB->name = devm_kzalloc(dev, 16, GFP_KERNEL); if (!pcdev_RGB->name) return -ENOMEM; sprintf((char *)pcdev_RGB->name, "sunxi_led%dr", i); pcdev_RGB->brightness = LED_OFF; pcdev_RGB->brightness_set_blocking = sunxi_set_led_brightness; pcdev_RGB->dev = dev; err = led_classdev_register(dev, pcdev_RGB); if (err < 0) { LED_ERR("led_classdev_register %s failed!\n", pcdev_RGB->name); return err; } led->pcdev_group[i].g.type = LED_TYPE_G; pcdev_RGB = &led->pcdev_group[i].g.cdev; pcdev_RGB->name = devm_kzalloc(dev, 16, GFP_KERNEL); if (!pcdev_RGB->name) return -ENOMEM; sprintf((char *)pcdev_RGB->name, "sunxi_led%dg", i); pcdev_RGB->brightness = LED_OFF; pcdev_RGB->brightness_set_blocking = sunxi_set_led_brightness; pcdev_RGB->dev = dev; err = led_classdev_register(dev, pcdev_RGB); if (err < 0) { LED_ERR("led_classdev_register %s failed!\n", pcdev_RGB->name); return err; } led->pcdev_group[i].b.type = LED_TYPE_B; pcdev_RGB = &led->pcdev_group[i].b.cdev; pcdev_RGB->name = devm_kzalloc(dev, 16, GFP_KERNEL); if (!pcdev_RGB->name) return -ENOMEM; sprintf((char *)pcdev_RGB->name, "sunxi_led%db", i); pcdev_RGB->brightness = LED_OFF; pcdev_RGB->brightness_set_blocking = sunxi_set_led_brightness; pcdev_RGB->dev = dev; err = led_classdev_register(dev, pcdev_RGB); if (err < 0) { LED_ERR("led_classdev_register %s failed!\n", pcdev_RGB->name); return err; } led->pcdev_group[i].led_num = i; } size = sizeof(u32) * led->led_count; led->data = devm_kzalloc(dev, size, GFP_KERNEL); if (!led->data) return -ENOMEM; return 0; } static void sunxi_unregister_led_classdev(struct sunxi_led *led) { int i; for (i = 0; i < led->led_count; i++) { kfree(led->pcdev_group[i].b.cdev.name); led->pcdev_group[i].b.cdev.name = NULL; kfree(led->pcdev_group[i].g.cdev.name); led->pcdev_group[i].g.cdev.name = NULL; kfree(led->pcdev_group[i].r.cdev.name); led->pcdev_group[i].r.cdev.name = NULL; led_classdev_unregister(&led->pcdev_group[i].b.cdev); led_classdev_unregister(&led->pcdev_group[i].g.cdev); led_classdev_unregister(&led->pcdev_group[i].r.cdev); } kfree(led->data); led->data = NULL; kfree(led->pcdev_group); led->pcdev_group = NULL; } static inline int sunxi_get_u32_of_property(const char *propname, int *val) { int err; struct sunxi_led *led = sunxi_led_global; struct device *dev = led->dev; struct device_node *np = dev->of_node; err = of_property_read_u32(np, propname, val); if (err < 0) LED_ERR("failed to get the value of propname %s!\n", propname); return err; } static inline int sunxi_get_str_of_property(const char *propname, const char **out_string) { int err; struct sunxi_led *led = sunxi_led_global; struct device *dev = led->dev; struct device_node *np = dev->of_node; err = of_property_read_string(np, propname, out_string); if (err < 0) LED_ERR("failed to get the string of propname %s!\n", propname); return err; } static void sunxi_get_para_of_property(struct sunxi_led *led) { int err; u32 val; const char *str; err = sunxi_get_u32_of_property("led_count", &val); if (!err) led->led_count = val; memcpy(led->output_mode.str, "GRB", 3); led->output_mode.val = SUNXI_OUTPUT_GRB; err = sunxi_get_str_of_property("output_mode", &str); if (!err) if (!strncmp(str, "BRG", 3) || !strncmp(str, "GBR", 3) || !strncmp(str, "RGB", 3) || !strncmp(str, "RBG", 3) || !strncmp(str, "BGR", 3)) memcpy(led->output_mode.str, str, 3); err = sunxi_get_str_of_property("led_regulator", &str); if (!err) { if (strlen(str) >= sizeof(led->regulator_id)) LED_ERR("illegal regulator id\n"); else { strcpy(led->regulator_id, str); pr_info("led_regulator: %s\n", led->regulator_id); } } err = sunxi_get_u32_of_property("reset_ns", &val); if (!err) led->reset_ns = val; err = sunxi_get_u32_of_property("t1h_ns", &val); if (!err) led->t1h_ns = val; err = sunxi_get_u32_of_property("t1l_ns", &val); if (!err) led->t1l_ns = val; err = sunxi_get_u32_of_property("t0h_ns", &val); if (!err) led->t0h_ns = val; err = sunxi_get_u32_of_property("t0l_ns", &val); if (!err) led->t0l_ns = val; err = sunxi_get_u32_of_property("wait_time0_ns", &val); if (!err) led->wait_time0_ns = val; err = sunxi_get_u32_of_property("wait_time1_ns", &val); if (!err) led->wait_time1_ns = val; err = sunxi_get_u32_of_property("wait_data_time_ns", &val); if (!err) led->wait_data_time_ns = val; } static void sunxi_led_set_all(struct sunxi_led *led, u8 channel, enum led_brightness value) { u32 i; struct led_classdev *led_cdev; if (channel%3 == 0) { for (i = 0; i < led->led_count; i++) { led_cdev = &led->pcdev_group[i].r.cdev; mutex_lock(&led_cdev->led_access); sunxi_set_led_brightness(led_cdev, value); mutex_unlock(&led_cdev->led_access); } } else if (channel%3 == 1) { for (i = 0; i < led->led_count; i++) { led_cdev = &led->pcdev_group[i].g.cdev; mutex_lock(&led_cdev->led_access); sunxi_set_led_brightness(led_cdev, value); mutex_unlock(&led_cdev->led_access); } } else { for (i = 0; i < led->led_count; i++) { led_cdev = &led->pcdev_group[i].b.cdev; mutex_lock(&led_cdev->led_access); sunxi_set_led_brightness(led_cdev, value); mutex_unlock(&led_cdev->led_access); } } } static ssize_t led_show(struct class *class, struct class_attribute *attr, char *buf) { struct sunxi_led *led = sunxi_led_global; sunxi_led_set_all(led, 0, 0); sunxi_led_set_all(led, 1, 0); sunxi_led_set_all(led, 2, 0); sunxi_led_set_all(led, 0, 20); msleep(500); sunxi_led_set_all(led, 1, 20); msleep(500); sunxi_led_set_all(led, 2, 20); msleep(500); sunxi_led_set_all(led, 0, 0); sunxi_led_set_all(led, 1, 0); sunxi_led_set_all(led, 2, 0); return 0; } static struct class_attribute led_class_attrs[] = { __ATTR(light, 0644, led_show, NULL), //__ATTR_NULL, }; static void led_node_init(void) { int i; int err; /* sys/class/led/xxx */ for (i = 0; i < ARRAY_SIZE(led_class_attrs); i++) { err = class_create_file(led_class, &led_class_attrs[i]); if (err) { LED_ERR("class_create_file() failed!\n"); while (i--) class_remove_file(led_class, &led_class_attrs[i]); class_destroy(led_class); led_class = NULL; } } } static int sunxi_led_probe(struct platform_device *pdev) { int err; struct sunxi_led *led; struct device *dev = &pdev->dev; struct resource *mem_res = NULL; int ret; dprintk(DEBUG_INIT, "start\n"); led = devm_kzalloc(dev, sizeof(struct sunxi_led), GFP_KERNEL); if (!led) return -ENOMEM; sunxi_led_global = led; platform_set_drvdata(pdev, led); led->dev = dev; mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (mem_res == NULL) { LED_ERR("failed to get MEM res\n"); ret = -ENXIO; goto emem; } if (!request_mem_region(mem_res->start, resource_size(mem_res), mem_res->name)) { LED_ERR("failed to request mem region\n"); ret = -EINVAL; goto emem; } led->iomem_reg_base = ioremap(mem_res->start, resource_size(mem_res)); if (!led->iomem_reg_base) { ret = -EIO; goto eiomap; } led->res = mem_res; led->output_mode.str = devm_kzalloc(dev, 3, GFP_KERNEL); if (!led->output_mode.str) { ret = -ENOMEM; goto ezalloc_str; } sunxi_get_para_of_property(led); err = led_regulator_request(led); if (err < 0) { LED_ERR("request regulator failed!\n"); ret = err; goto eregulator; } err = sunxi_register_led_classdev(led); if (err) { LED_ERR("failed to register led classdev\n"); ret = err; goto eclassdev; } sunxi_ledc_set_time(led); led->reset = devm_reset_control_get(&pdev->dev, NULL); if (IS_ERR(led->reset)) { LED_ERR("get reset clk error\n"); return -EINVAL; } ret = reset_control_deassert(led->reset); if (ret) { LED_ERR("deassert clk error, ret:%d\n", ret); return ret; } sunxi_clk_init(led); init_waitqueue_head(&led->wait); err = sunxi_ledc_irq_init(led); if (err) { LED_ERR("failed to init irq\n"); ret = err; goto eirq; } sunxi_ledc_pinctrl_init(led); #ifdef CONFIG_DEBUG_FS sunxi_led_create_debugfs(led); #endif /* CONFIG_DEBUG_FS */ led_class = class_create(THIS_MODULE, "led"); if (IS_ERR(led_class)) { LED_ERR("class_register err\n"); class_destroy(led_class); ret = -EFAULT; goto eclass; } led_node_init(); dprintk(DEBUG_INIT, "finish\n"); return 0; eclass: #ifdef CONFIG_DEBUG_FS sunxi_led_remove_debugfs(led); #endif /* CONFIG_DEBUG_FS */ sunxi_ledc_irq_deinit(led); eirq: sunxi_unregister_led_classdev(led); sunxi_clk_deinit(led); eclassdev: led_regulator_release(led); eregulator: kfree(led->output_mode.str); ezalloc_str: iounmap(led->iomem_reg_base); led->iomem_reg_base = NULL; eiomap: release_mem_region(mem_res->start, resource_size(mem_res)); emem: kfree(led); return ret; } static int sunxi_led_remove(struct platform_device *pdev) { struct sunxi_led *led = platform_get_drvdata(pdev); class_destroy(led_class); #ifdef CONFIG_DEBUG_FS sunxi_led_remove_debugfs(led); #endif /* CONFIG_DEBUG_FS */ if (led->dma_chan) { dmaengine_terminate_all(led->dma_chan); dma_release_channel(led->dma_chan); led->dma_chan = NULL; } sunxi_ledc_irq_deinit(led); sunxi_unregister_led_classdev(led); sunxi_clk_deinit(led); led_regulator_release(led); kfree(led->output_mode.str); led->output_mode.str = NULL; iounmap(led->iomem_reg_base); led->iomem_reg_base = NULL; release_mem_region(led->res->start, resource_size(led->res)); kfree(led); led = NULL; dprintk(DEBUG_INIT, "finish\n"); return 0; } #if IS_ENABLED(CONFIG_PM) static inline void sunxi_led_save_regs(struct sunxi_led *led) { int i; for (i = 0; i < ARRAY_SIZE(sunxi_led_regs_offset); i++) led->regs_backup[i] = readl(led->iomem_reg_base + sunxi_led_regs_offset[i]); } static inline void sunxi_led_restore_regs(struct sunxi_led *led) { int i; for (i = 0; i < ARRAY_SIZE(sunxi_led_regs_offset); i++) writel(led->regs_backup[i], led->iomem_reg_base + sunxi_led_regs_offset[i]); } static void sunxi_led_enable_irq(struct sunxi_led *led) { enable_irq(led->irqnum); } static void sunxi_led_disable_irq(struct sunxi_led *led) { disable_irq_nosync(led->irqnum); } static int sunxi_led_gpio_state_select(struct sunxi_led *led, char *name) { int err; struct pinctrl_state *pctrl_state; pctrl_state = pinctrl_lookup_state(led->pctrl, name); if (IS_ERR(pctrl_state)) { dev_err(led->dev, "pinctrl_lookup_state(%s) failed! return %p\n", name, pctrl_state); return PTR_ERR(pctrl_state); } err = pinctrl_select_state(led->pctrl, pctrl_state); if (err < 0) { dev_err(led->dev, "pinctrl_select_state(%s) failed! return %d\n", name, err); return err; } return 0; } static void sunxi_led_enable_clk(struct sunxi_led *led) { clk_prepare_enable(led->clk_ledc); clk_prepare_enable(led->clk_cpuapb); } static void sunxi_led_disable_clk(struct sunxi_led *led) { clk_disable_unprepare(led->clk_cpuapb); clk_disable_unprepare(led->clk_ledc); } static int sunxi_led_power_on(struct sunxi_led *led) { int err; if (led->regulator == NULL) return 0; err = regulator_enable(led->regulator); if (err) { dev_err(led->dev, "enable regulator %s failed!\n", led->regulator_id); return err; } return 0; } static int sunxi_led_power_off(struct sunxi_led *led) { int err; if (led->regulator == NULL) return 0; err = regulator_disable(led->regulator); if (err) { dev_err(led->dev, "disable regulator %s failed!\n", led->regulator_id); return err; } return 0; } static int sunxi_led_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct sunxi_led *led = platform_get_drvdata(pdev); dev_dbg(led->dev, "[%s] enter standby\n", __func__); sunxi_led_disable_irq(led); sunxi_led_save_regs(led); sunxi_led_gpio_state_select(led, PINCTRL_STATE_SLEEP); sunxi_led_disable_clk(led); reset_control_assert(led->reset); sunxi_led_power_off(led); return 0; } static int sunxi_led_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct sunxi_led *led = platform_get_drvdata(pdev); dev_dbg(led->dev, "[%s] return from standby\n", __func__); sunxi_led_power_on(led); reset_control_deassert(led->reset); sunxi_led_enable_clk(led); sunxi_led_gpio_state_select(led, PINCTRL_STATE_DEFAULT); sunxi_led_restore_regs(led); sunxi_led_enable_irq(led); return 0; } static const struct dev_pm_ops sunxi_led_pm_ops = { .suspend = sunxi_led_suspend, .resume = sunxi_led_resume, }; #define SUNXI_LED_PM_OPS (&sunxi_led_pm_ops) #endif static const struct of_device_id sunxi_led_dt_ids[] = { {.compatible = "allwinner,sunxi-leds"}, {}, }; static struct platform_driver sunxi_led_driver = { .probe = sunxi_led_probe, .remove = sunxi_led_remove, .driver = { .name = "sunxi-leds", .owner = THIS_MODULE, #if IS_ENABLED(CONFIG_PM) .pm = SUNXI_LED_PM_OPS, #endif .of_match_table = sunxi_led_dt_ids, }, }; module_platform_driver(sunxi_led_driver); module_param_named(debug, debug_mask, int, 0664); MODULE_ALIAS("sunxi leds dirver"); MODULE_ALIAS("platform : leds dirver"); MODULE_LICENSE("GPL v2"); MODULE_VERSION("1.2.3"); MODULE_AUTHOR("Albert Yu <yuxyun@allwinnertech.com>"); MODULE_AUTHOR("liuyu <SWCliuyus@allwinnertech.com>"); MODULE_DESCRIPTION("Allwinner ledc-controller driver");
请更新
leds-sunxi.c
修复了 LEDC DMA相关功能 -
@awwwwa 大佬这个驱动文件是tina里面用的还是Meils啊
我这边用的是meils4里面的
hal_ledc
那个驱动./ekernel/drivers/hal/source/ledc/hal_ledc.c
测试的是这段代码被我改了强行用cpu模式或者是DMA模式测试
CPU部分没问题可以驱动但是DMA部分不行
具体是哪里的BUG呀大佬,可以指点一下不,谢啦谢啦
void hal_ledc_trans_data(struct ledc_config *ledc) { int i; unsigned long int size; unsigned int mask = 0; struct dma_slave_config slave_config; mask = LEDC_TRANS_FINISH_INT_EN | LEDC_WAITDATA_TIMEOUT_INT_EN | LEDC_FIFO_OVERFLOW_INT_EN | LEDC_GLOBAL_INT_EN; int leoset=1; //SUNXI_LEDC_FIFO_DEPTH //if (ledc->length <= SUNXI_LEDC_FIFO_DEPTH) { //if (LEO_led_count <= SUNXI_LEDC_FIFO_DEPTH) { if (1) { //printf("------------------------------CPU mode %d ------------------------\n",ledc->length); ledc_info("trans data by CPU mode\n"); mask |= LEDC_FIFO_CPUREQ_INT_EN; ledc_reset_en(); hal_ledc_set_time(ledc); ledc_set_output_mode(ledc->output_mode); ledc_set_cpu_mode(); ledc_set_length(ledc->length); //ledc_set_length(LEO_led_count); ledc_enable_irq(mask); //for(i = 0; i < ledc->length; i++){ //for(i = 0; i < LEO_led_count; i++){ for(i = 0; i < ledc->led_count; i++){ ledc_set_data(ledc->data[i]); //printf("-----ledc->data[%d]->%d-----\n",i,ledc->data[i]); } ledc_enable(); } else { ledc_info("trans data by DMA mode\n"); //printf("------------------------------DMA mode %d------------------------\n",ledc->length); mask &= ~LEDC_FIFO_CPUREQ_INT_EN; ledc_reset_en(); size = ledc->led_count * 4; //size = ledc->length * 4; //size = LEO_led_count * 4; hal_dcache_clean((unsigned long)ledc->data, sizeof(ledc->data)); slave_config.direction = DMA_MEM_TO_DEV; slave_config.src_addr = (unsigned long)(ledc->data); slave_config.dst_addr = (uint32_t)(base_addr + LEDC_DATA_REG); slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; slave_config.src_maxburst = DMA_SLAVE_BURST_16; slave_config.dst_maxburst = DMA_SLAVE_BURST_16; slave_config.slave_id = sunxi_slave_id(DRQDST_LEDC, DRQSRC_SDRAM); hal_dma_slave_config(dma_chan, &slave_config); hal_dma_prep_device(dma_chan, slave_config.dst_addr, slave_config.src_addr, size, DMA_MEM_TO_DEV); //dma_chan->callback = ledc_dma_callback; hal_dma_start(dma_chan); hal_ledc_set_time(ledc); ledc_set_output_mode(ledc->output_mode); //ledc_set_length(ledc->length); ledc_set_length(ledc->led_count); //ledc_set_length(LEO_led_count); ledc_set_dma_mode(); ledc_enable_irq(mask); ledc_enable(); } }
-
@leomini5 这个是tina的
-
@whycanservice 那melis4 怎么办呀,大佬
-
#include <stdlib.h> #include <interrupt.h> #include <hal_atomic.h> #include <hal_gpio.h> #include <hal_dma.h> #include <hal_cache.h> #include <sunxi_hal_ledc.h> #ifdef CONFIG_COMPONENTS_PM #include <pm_devops.h> #endif /* define this macro when debugging is required */ /* #define LEDC_DEBUG */ #ifdef LEDC_DEBUG #define ledc_info(fmt, args...) printf("%s()%d - "fmt, __func__, __LINE__, ##args) #else #define ledc_info(fmt, args...) #endif #define led_err(fmt, args...) printf("%s()%d - "fmt, __func__, __LINE__, ##args) #define LEDC_PIN_SLEEP 0 struct ledc_config ledc_config = { .led_count = 3, .reset_ns = 84, .t1h_ns = 800, .t1l_ns = 450, .t0h_ns = 400, .t0l_ns = 850, .wait_time0_ns = 84, .wait_time1_ns = 84, .wait_data_time_ns = 600000, .output_mode = "GRB", }; static unsigned long base_addr = LEDC_BASE; struct sunxi_dma_chan *dma_chan; struct sunxi_led *led; static hal_irqreturn_t sunxi_ledc_irq_handler(void *dummy) { ledc_info("=======enter irq_handler=====\n"); struct sunxi_led *led = (struct sunxi_led *)dummy; unsigned int irq_status; irq_status = hal_ledc_get_irq_status(); hal_ledc_clear_all_irq(); if (irq_status & LEDC_TRANS_FINISH_INT) led->result = RESULT_COMPLETE; if (irq_status & LEDC_WAITDATA_TIMEOUT_INT) led->result = RESULT_ERR; if (irq_status & LEDC_FIFO_OVERFLOW_INT) led->result = RESULT_ERR; led->config.length = 0; hal_ledc_reset(); return HAL_IRQ_OK; } int sunxi_led_get_config(struct ledc_config *config) { *config = ledc_config; return 0; } static int ledc_clk_init(void) { hal_clk_type_t clk_type = HAL_SUNXI_CCU; hal_clk_id_t mod_clk_id = CLK_LEDC; hal_clk_id_t bus_clk_id = CLK_BUS_LEDC; hal_reset_type_t reset_type = HAL_SUNXI_RESET; #ifdef CONFIG_ARCH_SUN20IW2 hal_reset_id_t reset_id = RST_LEDC; #else hal_reset_id_t reset_id = RST_BUS_LEDC; #endif #ifdef CONFIG_ARCH_SUN8IW18P1 hal_clock_enable(CLK_BUS_LEDC); #else led->reset = hal_reset_control_get(reset_type, reset_id); if (hal_reset_control_deassert(led->reset)) { ledc_info("ledc reset deassert failed!"); return -1; } hal_reset_control_put(led->reset); led->mod_clk = hal_clock_get(clk_type, mod_clk_id); if (hal_clock_enable(led->mod_clk)) { ledc_info("ledc clk enable mclk failed!"); return -1; } led->bus_clk = hal_clock_get(clk_type, bus_clk_id); if (hal_clock_enable(led->bus_clk)) { ledc_info("ledc clk enable mclk failed!"); return -1; } #endif return 0; } static void ledc_clk_exit(void) { #ifdef CONFIG_ARCH_SUN8IW18P1 hal_clock_disable(CLK_BUS_LEDC); #else hal_clock_disable(led->bus_clk); hal_clock_disable(led->bus_clk); hal_reset_control_assert(led->reset); #endif } static int ledc_pinctrl_init(void) { if (hal_gpio_pinmux_set_function(LEDC_PIN, LEDC_PINMUXSEL)) { ledc_info("ledc pin set default function failed!"); return -1; } return 0; } static void ledc_pinctrl_exit(void) { hal_gpio_pinmux_set_function(LEDC_PIN, LEDC_PIN_SLEEP); } static void ledc_dump_reg(void) { ledc_info("LEDC_CTRL_REG = %0x\n", hal_readl(base_addr + LEDC_CTRL_REG)); ledc_info("LED_T01_TIMING_CTRL_REG = %0x\n", hal_readl(base_addr + LED_T01_TIMING_CTRL_REG)); ledc_info("LEDC_DATA_FINISH_CNT_REG = %0x\n", hal_readl(base_addr + LEDC_DATA_FINISH_CNT_REG)); ledc_info("LED_RST_TIMING_CTRL_REG = %0x\n", hal_readl(base_addr + LED_RST_TIMING_CTRL_REG)); ledc_info("LEDC_WAIT_TIME0_CTRL_REG = %0x\n", hal_readl(base_addr + LEDC_WAIT_TIME0_CTRL_REG)); ledc_info("LEDC_DATA_REG = %0x\n", hal_readl(base_addr + LEDC_DATA_REG)); ledc_info("LEDC_DMA_CTRL_REG = %0x\n", hal_readl(base_addr + LEDC_DMA_CTRL_REG)); ledc_info("LEDC_INTC_REG = %0x\n", hal_readl(base_addr + LEDC_INTC_REG)); ledc_info("LEDC_INTS_REG = %0x\n", hal_readl(base_addr + LEDC_INTS_REG)); ledc_info("LEDC_WAIT_TIME1_CTRL_REG = %0x\n", hal_readl(base_addr + LEDC_WAIT_TIME1_CTRL_REG)); ledc_info("LEDC_FIFO_DATA0_REG = %0x\n", hal_readl(base_addr + LEDC_FIFO_DATA0_REG)); } static void ledc_set_reset_ns(unsigned int reset_ns) { unsigned int n, reg_val; unsigned int mask = 0x1FFF; unsigned int min = LEDC_RESET_TIME_MIN_NS; unsigned int max = LEDC_RESET_TIME_MAX_NS; if (reset_ns < min || reset_ns > max) { ledc_info("invalid parameter, reset_ns should be %u-%u!\n", min, max); return; } n = (reset_ns - 42) / 42; reg_val = hal_readl(base_addr + LED_RST_TIMING_CTRL_REG); reg_val &= ~(mask << 16); reg_val |= (n << 16); hal_writel(reg_val, base_addr + LED_RST_TIMING_CTRL_REG); } static void ledc_set_t1h_ns(unsigned int t1h_ns) { unsigned int n, reg_val; unsigned int mask = 0x3F; unsigned int shift = 21; unsigned int min = LEDC_T1H_MIN_NS; unsigned int max = LEDC_T1H_MAX_NS; if (t1h_ns < min || t1h_ns > max) { ledc_info("invalid parameter, t1h_ns should be %u-%u!\n", min, max); return; } n = (t1h_ns - 42) / 42; reg_val = hal_readl(base_addr + LED_T01_TIMING_CTRL_REG); reg_val &= ~(mask << shift); reg_val |= n << shift; hal_writel(reg_val, base_addr + LED_T01_TIMING_CTRL_REG); } static void ledc_set_t1l_ns(unsigned int t1l_ns) { unsigned int n, reg_val; unsigned int mask = 0x1F; unsigned int shift = 16; unsigned int min = LEDC_T1L_MIN_NS; unsigned int max = LEDC_T1L_MAX_NS; if (t1l_ns < min || t1l_ns > max) { ledc_info("invalid parameter, t1l_ns should be %u-%u!\n", min, max); return; } n = (t1l_ns - 42) / 42; reg_val = hal_readl(base_addr + LED_T01_TIMING_CTRL_REG); reg_val &= ~(mask << shift); reg_val |= n << shift; hal_writel(reg_val, base_addr + LED_T01_TIMING_CTRL_REG); } static void ledc_set_t0h_ns(unsigned int t0h_ns) { unsigned int n, reg_val; unsigned int mask = 0x1F; unsigned int shift = 6; unsigned int min = LEDC_T0H_MIN_NS; unsigned int max = LEDC_T0H_MAX_NS; if (t0h_ns < min || t0h_ns > max) { ledc_info("invalid parameter, t0h_ns should be %u-%u!\n", min, max); return; } n = (t0h_ns - 42) / 42; reg_val = hal_readl(base_addr + LED_T01_TIMING_CTRL_REG); reg_val &= ~(mask << shift); reg_val |= n << shift; hal_writel(reg_val, base_addr + LED_T01_TIMING_CTRL_REG); } static void ledc_set_t0l_ns(unsigned int t0l_ns) { unsigned int n, reg_val; unsigned int min = LEDC_T0L_MIN_NS; unsigned int max = LEDC_T0L_MAX_NS; if (t0l_ns < min || t0l_ns > max) { ledc_info("invalid parameter, t0l_ns should be %u-%u!\n", min, max); return; } n = (t0l_ns - 42) / 42; reg_val = hal_readl(base_addr + LED_T01_TIMING_CTRL_REG); reg_val &= ~0x3F; reg_val |= n; hal_writel(reg_val, base_addr + LED_T01_TIMING_CTRL_REG); } static void ledc_set_wait_time0_ns(unsigned int wait_time0_ns) { unsigned int n, reg_val; unsigned int min = LEDC_WAIT_TIME0_MIN_NS; unsigned int max = LEDC_WAIT_TIME0_MAX_NS; if (wait_time0_ns < min || wait_time0_ns > max) { ledc_info("invalid parameter, wait_time0_ns should be %u-%u!\n", min, max); return; } n = (wait_time0_ns - 42) / 42; reg_val = (1 << 8) | n; hal_writel(reg_val, base_addr + LEDC_WAIT_TIME0_CTRL_REG); } static void ledc_set_wait_time1_ns(unsigned long long wait_time1_ns) { //unsigned long tmp; unsigned long long max = LEDC_WAIT_TIME1_MAX_NS; unsigned int min = LEDC_WAIT_TIME1_MIN_NS; unsigned int n, reg_val; if (wait_time1_ns < min || wait_time1_ns > max) { ledc_info("invalid parameter, wait_time1_ns should be %u-%llu!\n", min, max); return; } n = wait_time1_ns / 42; //tmp = wait_time1_ns; //n = div_u64(tmp, 42); n -= 1; reg_val = (1 << 31) | n; hal_writel(reg_val, base_addr + LEDC_WAIT_TIME1_CTRL_REG); } static void ledc_set_wait_data_time_ns(unsigned int wait_data_time_ns) { unsigned int mask = 0x1FFF; unsigned int shift = 16; unsigned int reg_val = 0; unsigned int n, min, max; min = LEDC_WAIT_DATA_TIME_MIN_NS; max = LEDC_WAIT_DATA_TIME_MAX_NS_IC; if (wait_data_time_ns < min || wait_data_time_ns > max) { ledc_info("invalid parameter, wait_data_time_ns should be %u-%u!\n", min, max); return; } n = (wait_data_time_ns - 42) / 42; reg_val &= ~(mask << shift); reg_val |= (n << shift); hal_writel(reg_val, base_addr + LEDC_DATA_FINISH_CNT_REG); } static void ledc_set_length(unsigned int length) { unsigned int reg_val; if (length == 0) return; reg_val = hal_readl(base_addr + LEDC_CTRL_REG); reg_val &= ~(0x1FFF << 16); reg_val |= length << 16; hal_writel(reg_val, base_addr + LEDC_CTRL_REG); reg_val = hal_readl(base_addr + LED_RST_TIMING_CTRL_REG); reg_val &= ~0x3FF; reg_val |= length - 1; hal_writel(reg_val, base_addr + LED_RST_TIMING_CTRL_REG); } static void ledc_set_output_mode(const char *str) { unsigned int val = 0; unsigned int mask = 0x7; unsigned int shift = 6; unsigned int reg_val ; if (str != NULL) { if (!strncmp(str, "GRB", 3)) val = LEDC_OUTPUT_GRB; else if (!strncmp(str, "GBR", 3)) val = LEDC_OUTPUT_GBR; else if (!strncmp(str, "RGB", 3)) val = LEDC_OUTPUT_RGB; else if (!strncmp(str, "RBG", 3)) val = LEDC_OUTPUT_RBG; else if (!strncmp(str, "BGR", 3)) val = LEDC_OUTPUT_BGR; else if (!strncmp(str, "BRG", 3)) val = LEDC_OUTPUT_BRG; else return; } else { } reg_val = hal_readl(base_addr + LEDC_CTRL_REG); reg_val &= ~(mask << shift); reg_val |= val; hal_writel(reg_val, base_addr + LEDC_CTRL_REG); } static void ledc_disable_irq(unsigned int mask) { unsigned int reg_val = 0; reg_val = hal_readl(base_addr + LEDC_INTC_REG); reg_val &= ~mask; hal_writel(reg_val, base_addr + LEDC_INTC_REG); } static void ledc_enable_irq(unsigned int mask) { unsigned int reg_val = 0; reg_val = hal_readl(base_addr + LEDC_INTC_REG); reg_val |= mask; hal_writel(reg_val, base_addr + LEDC_INTC_REG); } static void ledc_set_dma_mode(void) { unsigned int reg_val = 0; reg_val |= 1 << 5; hal_writel(reg_val, base_addr + LEDC_DMA_CTRL_REG); } static void ledc_set_cpu_mode(void) { unsigned int reg_val = 0; reg_val &= ~(1 << 5); hal_writel(reg_val, base_addr + LEDC_DMA_CTRL_REG); } static void ledc_clear_all_irq(void) { unsigned int reg_val; reg_val = hal_readl(base_addr + LEDC_INTS_REG); reg_val |= 0x1F; hal_writel(reg_val, base_addr + LEDC_INTS_REG); } static unsigned int ledc_get_irq_status(void) { return hal_readl(base_addr + LEDC_INTS_REG); } static void ledc_soft_reset(void) { unsigned int reg_val; reg_val = hal_readl(base_addr + LEDC_CTRL_REG); reg_val |= 1 << 1; hal_writel(reg_val, base_addr + LEDC_CTRL_REG); } static void ledc_reset_en(void) { unsigned int reg_val; reg_val = hal_readl(base_addr + LEDC_CTRL_REG); reg_val |= 1 << 10; hal_writel(reg_val, base_addr + LEDC_CTRL_REG); } static void ledc_set_data(unsigned int data) { hal_writel(data, base_addr + LEDC_DATA_REG); } static void ledc_enable(void) { unsigned int reg_val; reg_val = hal_readl(base_addr + LEDC_CTRL_REG); reg_val |= 1; hal_writel(reg_val, base_addr + LEDC_CTRL_REG); } static void hal_ledc_set_time(struct ledc_config *ledc) { ledc_set_reset_ns(ledc->reset_ns); ledc_set_t1h_ns(ledc->t1h_ns); ledc_set_t1l_ns(ledc->t1l_ns); ledc_set_t0h_ns(ledc->t0h_ns); ledc_set_t0l_ns(ledc->t0l_ns); ledc_set_wait_time0_ns(ledc->wait_time0_ns); ledc_set_wait_time1_ns(ledc->wait_time1_ns); ledc_set_wait_data_time_ns(ledc->wait_data_time_ns); } void hal_ledc_dma_callback(void *para) { printf("dma callback\n"); } void hal_ledc_trans_data(struct ledc_config *ledc) { int i; unsigned long int size; unsigned int mask = 0; struct dma_slave_config slave_config; mask = LEDC_TRANS_FINISH_INT_EN | LEDC_WAITDATA_TIMEOUT_INT_EN | LEDC_FIFO_OVERFLOW_INT_EN | LEDC_GLOBAL_INT_EN; if (ledc->length <= SUNXI_LEDC_FIFO_DEPTH) { ledc_info("trans data by CPU mode\n"); mask |= LEDC_FIFO_CPUREQ_INT_EN; ledc_reset_en(); hal_ledc_set_time(ledc); ledc_set_output_mode(ledc->output_mode); ledc_set_cpu_mode(); ledc_set_length(ledc->length); ledc_enable_irq(mask); for(i = 0; i < ledc->length; i++) ledc_set_data(ledc->data[i]); ledc_enable(); } else { ledc_info("trans data by DMA mode\n"); mask &= ~LEDC_FIFO_CPUREQ_INT_EN; ledc_reset_en(); size = ledc->length * 4; hal_dcache_clean((unsigned long)ledc->data, sizeof(ledc->data)); slave_config.direction = DMA_MEM_TO_DEV; slave_config.src_addr = (unsigned long)(ledc->data); slave_config.dst_addr = (uint32_t)(base_addr + LEDC_DATA_REG); slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; slave_config.src_maxburst = DMA_SLAVE_BURST_16; slave_config.dst_maxburst = DMA_SLAVE_BURST_16; slave_config.slave_id = sunxi_slave_id(DRQDST_LEDC, DRQSRC_SDRAM); hal_dma_slave_config(dma_chan, &slave_config); hal_dma_prep_device(dma_chan, slave_config.dst_addr, slave_config.src_addr, size, DMA_MEM_TO_DEV); //dma_chan->callback = ledc_dma_callback; hal_dma_start(dma_chan); hal_ledc_set_time(ledc); ledc_set_output_mode(ledc->output_mode); ledc_set_length(ledc->length); ledc_set_dma_mode(); ledc_enable_irq(mask); ledc_enable(); } } void hal_ledc_clear_all_irq(void) { ledc_clear_all_irq(); } unsigned int hal_ledc_get_irq_status(void) { return ledc_get_irq_status(); } void hal_ledc_reset(void) { ledc_disable_irq(LEDC_TRANS_FINISH_INT_EN | LEDC_WAITDATA_TIMEOUT_INT_EN | LEDC_FIFO_OVERFLOW_INT_EN | LEDC_GLOBAL_INT_EN | LEDC_GLOBAL_INT_EN); if (dma_chan) { hal_dma_stop(dma_chan); } ledc_soft_reset(); } #ifdef CONFIG_COMPONENTS_PM static inline void sunxi_ledc_save_regs(struct sunxi_led *led) { int i; for (i = 0; i < ARRAY_SIZE(sunxi_ledc_regs_offset); i++) led->regs_backup[i] = readl(base_addr + sunxi_ledc_regs_offset[i]); } static inline void sunxi_ledc_restore_regs(struct sunxi_led *led) { int i; for (i = 0; i < ARRAY_SIZE(sunxi_ledc_regs_offset); i++) writel(led->regs_backup[i], base_addr + sunxi_ledc_regs_offset[i]); } static int hal_ledc_resume(struct pm_device *dev, suspend_mode_t mode) { int ret = 0; if (ledc_clk_init()) { led_err("ledc clk init failed \n"); ret = -1; goto err0; } if (ledc_pinctrl_init()) { led_err("ledc pinctrl init failed \n"); ret = -1; goto err1; } sunxi_ledc_restore_regs(led); hal_enable_irq(SUNXI_IRQ_LEDC); ledc_info("hal ledc resume"); return 0; err1: ledc_clk_exit(); err0: return ret; } static int hal_ledc_suspend(struct pm_device *dev, suspend_mode_t mode) { hal_disable_irq(SUNXI_IRQ_LEDC); sunxi_ledc_save_regs(led); ledc_pinctrl_exit(); ledc_clk_exit(); ledc_info("hal ledc suspend"); return 0; } struct pm_devops pm_ledc_ops = { .suspend = hal_ledc_suspend, .resume = hal_ledc_resume, }; struct pm_device pm_ledc = { .name = "sunxi_ledc", .ops = &pm_ledc_ops, }; #endif int hal_ledc_init(void) { ledc_info("hal_led_init\n"); led = malloc(sizeof(struct sunxi_led)); if (NULL == led) { led_err("sunxi led malloc err\n"); return -1; } sunxi_led_get_config(&led->config); led->config.data = malloc(sizeof(unsigned int) * led->config.led_count); if (NULL == led->config.data) { led_err("sunxi led config data malloc err\n"); goto err1; } if (ledc_clk_init()) { led_err("ledc clk init failed \n"); } if (ledc_pinctrl_init()) { led_err("ledc pinctrl init failed \n"); } hal_dma_chan_request(&dma_chan); if (hal_request_irq(SUNXI_IRQ_LEDC, sunxi_ledc_irq_handler, "ledc", led) < 0) { led_err("ledc request irq failed \n"); goto errirq; } hal_enable_irq(SUNXI_IRQ_LEDC); #ifdef CONFIG_COMPONENTS_PM pm_devops_register(&pm_ledc); #endif ledc_info("hal_led_init success\n"); return 0; errirq: free(led->config.data); err1: free(led); return -1; } void hal_ledc_deinit(void) { #ifdef CONFIG_COMPONENTS_PM pm_devops_unregister(&pm_ledc); #endif hal_disable_irq(SUNXI_IRQ_LEDC); hal_free_irq(SUNXI_IRQ_LEDC); hal_dma_chan_free(dma_chan); ledc_pinctrl_exit(); ledc_clk_exit(); free(led->config.data); free(led); } int sunxi_set_all_led(unsigned int brightness) { int i; led->config.length = led->config.led_count; for(i = 0;i < led->config.led_count;i++) led->config.data[i] = brightness; hal_ledc_trans_data(&led->config); return 0; } int sunxi_set_led_brightness(int led_num, unsigned int brightness) { u32 reg_val; if (NULL == led) { led_err("err : ledc is not init\n"); return -1; } if (led_num > led->config.led_count) { led_err("has not the %d led\n", led_num); return -1; } led->config.length = 1; led->config.data[led_num-1] = brightness; hal_ledc_trans_data(&led->config); reg_val = hal_ledc_get_irq_status(); ledc_info("ledc interrupt status reg is %x", reg_val); return 0; }
-
@awwwwa 谢啦谢啦
-
@awwwwa 我回去试了,这个ledc还是有问题,fifo模式下就两个灯亮,问题出在那个写入了,数据写的不对,然后DMA模式下还是不亮,我先凑合用吧,现在问题最大的是那个Wi-Fi Xr829 在Melis上没法正常工作……
-
使用dma模式搬运数据,需要打上如下补丁:
ledc在dma模式的代码存在问题,已修复该问题,ledc的dma通道之前也没有支持,需要添加上
diff --git a/hal/source/dma/platform/dma-sun8iw20.h b/hal/source/dma/platform/dma-sun8iw20.h index 2140014..fc5c72e 100644 --- a/hal/source/dma/platform/dma-sun8iw20.h +++ b/hal/source/dma/platform/dma-sun8iw20.h @@ -136,6 +136,7 @@ #define DRQDST_OTG_EP3 32 #define DRQDST_OTG_EP4 33 #define DRQDST_OTG_EP5 34 +#define DRQDST_LEDC 42 #define DRQDST_TWI0_TX 43 #define DRQDST_TWI1_TX 44 #define DRQDST_TWI2_TX 45 diff --git a/hal/source/ledc/hal_ledc.c b/hal/source/ledc/hal_ledc.c index 5499452..ddf471f 100755 --- a/hal/source/ledc/hal_ledc.c +++ b/hal/source/ledc/hal_ledc.c @@ -20,9 +20,10 @@ #define led_err(fmt, args...) printf("%s()%d - "fmt, __func__, __LINE__, ##args) #define LEDC_PIN_SLEEP 0 +#define LEDC_DMA_BUF_SIZE 4096 struct ledc_config ledc_config = { - .led_count = 3, + .led_count = 1024, .reset_ns = 84, .t1h_ns = 800, .t1l_ns = 450, @@ -37,6 +38,7 @@ static unsigned long base_addr = LEDC_BASE; struct sunxi_dma_chan *dma_chan; struct sunxi_led *led; +static uint8_t already_init; static hal_irqreturn_t sunxi_ledc_irq_handler(void *dummy) { @@ -448,15 +450,16 @@ void hal_ledc_dma_callback(void *para) { - printf("dma callback\n"); + ledc_info("dma transfer end\n"); } -void hal_ledc_trans_data(struct ledc_config *ledc) +int hal_ledc_trans_data(struct ledc_config *ledc) { - int i; + int i, ret; unsigned long int size; unsigned int mask = 0; struct dma_slave_config slave_config; + unsigned int const *buf = ledc->data; mask = LEDC_TRANS_FINISH_INT_EN | LEDC_WAITDATA_TIMEOUT_INT_EN | LEDC_FIFO_OVERFLOW_INT_EN | LEDC_GLOBAL_INT_EN; @@ -480,23 +483,32 @@ ledc_reset_en(); size = ledc->length * 4; + if (size <= LEDC_DMA_BUF_SIZE) { + memcpy(ledc->align_dma_buf, buf, ledc->length); + buf = ledc->align_dma_buf; + } - hal_dcache_clean((unsigned long)ledc->data, sizeof(ledc->data)); + hal_dcache_clean((unsigned long)buf, size); slave_config.direction = DMA_MEM_TO_DEV; - slave_config.src_addr = (unsigned long)(ledc->data); + slave_config.src_addr = (unsigned long)buf; slave_config.dst_addr = (uint32_t)(base_addr + LEDC_DATA_REG); slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - slave_config.src_maxburst = DMA_SLAVE_BURST_16; - slave_config.dst_maxburst = DMA_SLAVE_BURST_16; + slave_config.src_maxburst = DMA_SLAVE_BURST_4; + slave_config.dst_maxburst = DMA_SLAVE_BURST_4; slave_config.slave_id = sunxi_slave_id(DRQDST_LEDC, DRQSRC_SDRAM); - hal_dma_slave_config(dma_chan, &slave_config); + ret = hal_dma_slave_config(dma_chan, &slave_config); + if (ret) { + led_err("dma slave config failed\n"); + return -1; + } - hal_dma_prep_device(dma_chan, slave_config.dst_addr, slave_config.src_addr, size, DMA_MEM_TO_DEV); - - //dma_chan->callback = ledc_dma_callback; - hal_dma_start(dma_chan); + ret = hal_dma_prep_device(dma_chan, slave_config.dst_addr, slave_config.src_addr, size, DMA_MEM_TO_DEV); + if (ret) { + led_err("dma prep device failed\n"); + return -1; + } hal_ledc_set_time(ledc); ledc_set_output_mode(ledc->output_mode); @@ -504,7 +516,15 @@ ledc_set_dma_mode(); ledc_enable_irq(mask); ledc_enable(); + + dma_chan->callback = hal_ledc_dma_callback; + ret = hal_dma_start(dma_chan); + if (ret) { + led_err("dma start trans failed\n"); + return -1; + } } + return 0; } void hal_ledc_clear_all_irq(void) @@ -598,6 +618,14 @@ int hal_ledc_init(void) { + if (already_init) { + already_init++; + ledc_info("ledc has been inited, return ok\n"); + return 0; + } + + int ret, i; + ledc_info("hal_led_init\n"); led = malloc(sizeof(struct sunxi_led)); @@ -611,25 +639,40 @@ led->config.data = malloc(sizeof(unsigned int) * led->config.led_count); if (NULL == led->config.data) { led_err("sunxi led config data malloc err\n"); - goto err1; + goto err0; } + for(i = 0;i < led->config.led_count;i++) + led->config.data[i] = 0; if (ledc_clk_init()) { led_err("ledc clk init failed \n"); + goto err1; } if (ledc_pinctrl_init()) { led_err("ledc pinctrl init failed \n"); + goto err2; } - hal_dma_chan_request(&dma_chan); + ret = hal_dma_chan_request(&dma_chan); + if (ret == HAL_DMA_CHAN_STATUS_BUSY) + { + led_err("request dma_chan failed\n"); + goto err3; + } + led->config.align_dma_buf = dma_alloc_coherent(LEDC_DMA_BUF_SIZE); + if (!led->config.align_dma_buf) + { + led_err("alloc dma memory failed\n"); + goto err4; + } if (hal_request_irq(SUNXI_IRQ_LEDC, sunxi_ledc_irq_handler, "ledc", led) < 0) { led_err("ledc request irq failed \n"); - goto errirq; + goto err5; } hal_enable_irq(SUNXI_IRQ_LEDC); @@ -638,13 +681,22 @@ pm_devops_register(&pm_ledc); #endif + already_init++; ledc_info("hal_led_init success\n"); return 0; -errirq: - free(led->config.data); +err5: + dma_free_coherent(led->config.align_dma_buf); +err4: + hal_dma_chan_free(dma_chan); +err3: + ledc_pinctrl_exit(); +err2: + ledc_clk_exit(); err1: + free(led->config.data); +err0: free(led); return -1; @@ -652,27 +704,35 @@ void hal_ledc_deinit(void) { + if (already_init > 0) { + if (--already_init == 0) { #ifdef CONFIG_COMPONENTS_PM - pm_devops_unregister(&pm_ledc); + pm_devops_unregister(&pm_ledc); #endif - hal_disable_irq(SUNXI_IRQ_LEDC); - hal_free_irq(SUNXI_IRQ_LEDC); - hal_dma_chan_free(dma_chan); - ledc_pinctrl_exit(); - ledc_clk_exit(); - free(led->config.data); - free(led); + hal_disable_irq(SUNXI_IRQ_LEDC); + hal_free_irq(SUNXI_IRQ_LEDC); + dma_free_coherent(led->config.align_dma_buf); + hal_dma_chan_free(dma_chan); + ledc_pinctrl_exit(); + ledc_clk_exit(); + free(led->config.data); + free(led); + } + } } int sunxi_set_all_led(unsigned int brightness) { - int i; + int i, ret; led->config.length = led->config.led_count; for(i = 0;i < led->config.led_count;i++) led->config.data[i] = brightness; - hal_ledc_trans_data(&led->config); + ret = hal_ledc_trans_data(&led->config); + if (ret) { + led_err("ledc trans data error\n"); + } return 0; } @@ -680,6 +740,7 @@ int sunxi_set_led_brightness(int led_num, unsigned int brightness) { u32 reg_val; + int i, ret; if (NULL == led) { led_err("err : ledc is not init\n"); @@ -691,10 +752,16 @@ return -1; } - led->config.length = 1; + led->config.length = led_num; led->config.data[led_num-1] = brightness; - hal_ledc_trans_data(&led->config); + for (i = 0; i < led_num; i++) + ledc_info("the %d led light is %u\n", i + 1, led->config.data[i]); + + ret = hal_ledc_trans_data(&led->config); + if (ret) { + led_err("ledc trans data error\n"); + } reg_val = hal_ledc_get_irq_status(); ledc_info("ledc interrupt status reg is %x", reg_val); diff --git a/hal/source/ledc/platform_ledc.h b/hal/source/ledc/platform_ledc.h index 39f6933..2fa9c38 100644 --- a/hal/source/ledc/platform_ledc.h +++ b/hal/source/ledc/platform_ledc.h @@ -33,8 +33,6 @@ #ifndef __PLATFORM_LEDC_H__ #define __PLATFORM_LEDC_H__ -#define DRQDST_LEDC 43 - #if defined(CONFIG_ARCH_SUN8IW18P1) #include "platform/ledc_sun8iw18.h" #endif diff --git a/hal/test/ledc/test_ledc.c b/hal/test/ledc/test_ledc.c index 1ade796..79694c0 100755 --- a/hal/test/ledc/test_ledc.c +++ b/hal/test/ledc/test_ledc.c @@ -20,6 +20,7 @@ int ledc_test(int argc, char **argv) { int brightness = 0; + int led_num; printf("========LEDC TEST========\n"); @@ -32,9 +33,14 @@ return 0; } - brightness = atoi(argv[2]); + led_num = atoi(argv[1]); + if (led_num < 1 || led_num > 1024) + { + printf("The led_num you entered should be between 1 and 1024\n"); + } + brightness = atoi(argv[3]); - switch(argv[1][0]) + switch(argv[2][0]) { case 'R' : brightness <<= 8; break; case 'G' : brightness <<= 16; break; @@ -43,7 +49,8 @@ return -1; } - sunxi_set_led_brightness(1, brightness); + sunxi_set_led_brightness(led_num, brightness); + printf("led is %d\n", led_num); printf("brightness is %d\n", brightness); return 0; diff --git a/include/hal/sunxi_hal_ledc.h b/include/hal/sunxi_hal_ledc.h index a386338..e5a70d1 100644 --- a/include/hal/sunxi_hal_ledc.h +++ b/include/hal/sunxi_hal_ledc.h @@ -43,6 +43,7 @@ unsigned long long wait_time1_ns; unsigned int wait_data_time_ns; char *output_mode; + unsigned int *align_dma_buf; unsigned int *data; unsigned int length; }; @@ -86,7 +87,7 @@ int hal_ledc_init(void); void hal_ledc_deinit(void); -void hal_ledc_trans_data(struct ledc_config *ledc); +int hal_ledc_trans_data(struct ledc_config *ledc); void hal_ledc_clear_all_irq(void); unsigned int hal_ledc_get_irq_status(void); void hal_ledc_dma_callback(void *para);
DMA下 LED 颜色异常
diff --git a/hal/source/ledc/hal_ledc.c b/hal/source/ledc/hal_ledc.c index ddf471f4..8d818662 100755 --- a/hal/source/ledc/hal_ledc.c +++ b/hal/source/ledc/hal_ledc.c @@ -308,11 +308,15 @@ static void ledc_set_wait_data_time_ns(unsigned int wait_data_time_ns) hal_writel(reg_val, base_addr + LEDC_DATA_FINISH_CNT_REG); } +/* + * set the num of leds on the led-strip + * max support up to 1024 leds + */ static void ledc_set_length(unsigned int length) { unsigned int reg_val; - if (length == 0) + if (length == 0 || length > 1024) return; reg_val = hal_readl(base_addr + LEDC_CTRL_REG); @@ -721,12 +725,22 @@ void hal_ledc_deinit(void) } } -int sunxi_set_all_led(unsigned int brightness) +/* + * set the brightness of all the leds in led-strip to a uniform value + * @length: the num of leds on led-strip + * @brightness: the brightness data + */ +int sunxi_set_all_led(unsigned int length, unsigned int brightness) { int i, ret; - led->config.length = led->config.led_count; - for(i = 0;i < led->config.led_count;i++) + if (length > led->config.led_count) { + led_err("%d: max support 1024 leds\n", length); + return -1; + } + + led->config.length = length; + for(i = 0;i < led->length;i++) led->config.data[i] = brightness; ret = hal_ledc_trans_data(&led->config); @@ -737,7 +751,13 @@ int sunxi_set_all_led(unsigned int brightness) return 0; } -int sunxi_set_led_brightness(int led_num, unsigned int brightness) +/* + * set the brightness of each led on the led strip + * @length: all the num of leds on the led strip + * @led_num: the specified led that you want to set + * @brightness: the led brightness data + */ +int sunxi_set_led_brightness(unsigned int length, unsigned int led_num, unsigned int brightness) { u32 reg_val; int i, ret; @@ -747,12 +767,13 @@ int sunxi_set_led_brightness(int led_num, unsigned int brightness) return -1; } - if (led_num > led->config.led_count) { + if (length > led->config.led_count || len_num > length) { led_err("has not the %d led\n", led_num); return -1; } - led->config.length = led_num; + led->config.length = length; + /* set the specified led brightness, others set default brightness: 0x0*/ led->config.data[led_num-1] = brightness; for (i = 0; i < led_num; i++)
Copyright © 2024 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号