导航

    全志在线开发者论坛

    • 注册
    • 登录
    • 搜索
    • 版块
    • 话题
    • 在线文档
    • 社区主页

    D1s DMA驱动Ledc 问题

    MR Series
    3
    9
    3088
    正在加载更多帖子
    • 从旧到新
    • 从新到旧
    • 最多赞同
    回复
    • 在新帖中回复
    登录后回复
    此主题已被删除。只有拥有主题管理权限的用户可以查看。
    • L
      leomini5 LV 6 最后由 编辑

      各位大佬有谁用过D1S的Ledc驱动器啊,我用的是Meils4
      ,在测试Ledc驱动器的时候,这个ledc有两种模式,一个是CPU,一个是DMA

      灯用的是ws2812
      CPU驱动模式一下,Led可以亮但是,那个刷新LED数据比较快的情况下LED头尾的灯颜色就不正常了,最短间隔150ms,这个速度想要做点多灯的驱动肯定不行
      于是修改了led的配置参数,我把那个wait data那个时间修改后,就可以刷更快了,但是具体那个时许参数是什么作用呢?

      然后还有个问题那个Ledc换了DMA模式以后就直接全黑了,没输出了,这又是为啥啊,没有DMA,全靠cpu的话没法用啊……

      1 条回复 最后回复 回复 引用 分享 0
      • A
        awwwwa LV 8 最后由 编辑

        // 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(&current_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相关功能

        L 1 条回复 最后回复 回复 引用 分享 1
        • L
          leomini5 LV 6 @awwwwa 最后由 编辑

          @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();
          	}
          }
          
          WhycanService 1 条回复 最后回复 回复 引用 分享 0
          • WhycanService
            WhycanService LV 8 @leomini5 最后由 编辑

            @leomini5 这个是tina的

            L 1 条回复 最后回复 回复 引用 分享 0
            • L
              leomini5 LV 6 @WhycanService 最后由 编辑

              @whycanservice 那melis4 怎么办呀,大佬

              1 条回复 最后回复 回复 引用 分享 0
              • A
                awwwwa LV 8 最后由 编辑

                #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;
                }
                
                
                L 2 条回复 最后回复 回复 引用 分享 0
                • L
                  leomini5 LV 6 @awwwwa 最后由 编辑

                  @awwwwa 谢啦谢啦

                  1 条回复 最后回复 回复 引用 分享 0
                  • L
                    leomini5 LV 6 @awwwwa 最后由 编辑

                    @awwwwa 我回去试了,这个ledc还是有问题,fifo模式下就两个灯亮,问题出在那个写入了,数据写的不对,然后DMA模式下还是不亮,我先凑合用吧,现在问题最大的是那个Wi-Fi Xr829 在Melis上没法正常工作……

                    1 条回复 最后回复 回复 引用 分享 0
                    • A
                      awwwwa LV 8 最后由 awwwwa 编辑

                      使用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++)
                      
                      1 条回复 最后回复 回复 引用 分享 0
                      • 1 / 1
                      • First post
                        Last post

                      Copyright © 2024 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号

                      行为准则 | 用户协议 | 隐私权政策