Lighting Development Based on RK3308

Last Updated on : 2022-03-02 02:05:58download

The Tuya AVS SDK LED lighting effect driver framework is based on the Linux LED Class extension. Tuya has added an AVSUX Class to the standard Linux LED driver, which implements most of the AVS lighting effect processing logic, which greatly reduces the complexity of lighting effect driver development. You only need to implement a simple LED brightness setting. The interface can realize the lighting effect control of AVS.

Drive Framework

Before proceeding with driver migration, you need to understand two important data structures in the driver framework:

  • struct led_avsux_animation: Corresponds to a complete LED light effect animation, each animation is composed of several frames.
  • struct led_avsux_pattern: corresponds to a frame in the LED light effect animation, the driver updates the light effect in units of frames.

LED driver migration and development

After getting the original driver of the LED chip, you can transplant it as follows:

  • Copy the attached linux/drivers/leds/led-class-avsux.c and include/linux/led-class-avsux.h to the corresponding kernel directory.

  • Modify the include/linux/leds.h header file and add LED_DEV_CAP_AVS_UX.

    +++ b/include/linux/leds.h
    @@ -48,6 +48,7 @@ struct led_classdev {
    	#define SET_BRIGHTNESS_ASYNC (1 << 21)
    	#define SET_BRIGHTNESS_SYNC  (1 << 22)
    	#define LED_DEV_CAP_FLASH    (1 << 23)
    +   #define LED_DEV_CAP_AVS_UX   (1 << 24)
    
    /* Set LED brightness level */
    /* Must not sleep, use a workqueue if needed */
    

    Add AVSUX Class driver framework support in Makefile and Kconfig.

    +++ b/drivers/leds/Makefile
    @@ -3,6 +3,7 @@
    obj-$(CONFIG_NEW_LEDS) += led-core.o
    obj-$(CONFIG_LEDS_CLASS) += led-class.o
    obj-$(CONFIG_LEDS_CLASS_FLASH) += led-class-flash.o
    +obj-$(CONFIG_LEDS_CLASS_AVS_UX) += led-class-avsux.o
    obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
    +++ b/drivers/leds/Kconfig
    @@ -29,6 +29,16 @@ config LEDS_CLASS_FLASH
    for the flash related features of a LED device. It can be built
    as a module.
    
    +config LEDS_CLASS_AVS_UX
    +    tristate "Amazon Alexa Voice Service LED UX Class Support"
    +        depends on LEDS_CLASS
    +        help
    +          This option enables the amazon alexa voice service led sysfs class in /sys/class/leds.
    +          It wrapps LED Class and adds AVS ux LEDs specific sysfs attributes
    +          and kernel internal API to it. You'll need this to provide support
    +          for the AVS related features of a LED device. It can be built
    +          as a module.
    +
    comment "LED drivers"
    
  • After completing the above steps, you can use Tuya AVS LED driver framework to develop lighting effect drivers.

    LED driver development is very simple, you only need to implement the brightness_set interface and call the led_classdev_avsux_register function to register to the driver framework.

    A typical LED brightness setting interface is as follows:

    static void led_avs_brightness_set(struct led_classdev *led_cdev,
    enum led_brightness value)
    {
    	struct led_classdev_avsux *auled_cdev = lcdev_to_aucdev(led_cdev);
    	struct led_avsux_animation *animation = auled_cdev->cur_anime;
    	struct led_avsux_pattern *pattern = animation->cur_pattern;
    	int max = led_cdev->max_brightness;
    	u8 red, green, blue;
    	int i;
    
    	if (value > max)
    		value = max;
    
    	/* Set the RGB color of each light */
    	for (i = 0; i < auled_cdev->num_leds; i++) {
    		red = pattern->colors[i].red * value / max;
    		green = pattern->colors[i].green * value / max;
    		blue = pattern->colors[i].blue * value / max;
    		led_avs_set_rgb(i, red, green, blue);
    	}
    }
    
  • Then register the driver to the Tuya AVS LED driver framework, here is an I2C interface chip as an example.

    static int led_avs_i2c_probe(struct i2c_client *i2c,
    					const struct i2c_device_id *id) {
    	struct led_avs_priv *avs = NULL;
    	struct device_node *np = i2c->dev.of_node;
    	struct led_classdev *led_cdev;
    	int ret = -1;
    
    	avs = devm_kzalloc(&i2c->dev, sizeof(*avs), GFP_KERNEL);
    	if (avs == NULL)
    		return -ENOMEM;
    
    	avs->i2c = i2c;
    	avs->dev = &i2c->dev;
    
    	if (np) {
    		ret = avs_parse_dt(&i2c->dev, aw20036, np);
    		if (ret) {
    			dev_err(&i2c->dev,
    				"%s: failed to parse device tree node\n",
    				__func__);
    			return -EIO;
    		}
    	} else {
    		avs->reset_gpio = -1;
    	}
    
    	if (gpio_is_valid(avs->reset_gpio)) {
    		ret = devm_gpio_request_one(&i2c->dev, avs->reset_gpio,
    									GPIOF_OUT_INIT_LOW, "avs_rst");
    		if (ret) {
    			dev_err(&i2c->dev, "%s: rst request failed\n",
    					__func__);
    			return -EIO;
    		}
    	}
    
    	avs->regmap = devm_regmap_init_i2c(i2c, &avs_regmap);
    	if (IS_ERR(avs->regmap)) {
    		ret = PTR_ERR(avs->regmap);
    		dev_err(&i2c->dev,
    				"Failed to allocate register map: %d\n", ret);
    		return ret;
    	}
    
    	avs_hw_reset(avs);
    	ret = avs_check_chip_id(avs);
    	if (ret < 0) {
    		dev_err(&i2c->dev, "%s: check chip id failed ret=%d\n",
    				__func__, ret);
    	}
    
    	avs_led_init(avs);
    
        /* Set the type and number of lights */
    	avs->aucdev.led_type = AVS_UX_TYPE_RADIAL,
    	avs->aucdev.num_leds = 12,
    	led_cdev = &aw20036->aucdev.led_cdev;
    	led_cdev->name = AVS_I2C_NAME;
    	led_cdev->max_brightness = MAX_BRIGHTNESS; /* Adjust the overall brightness through max brightnees */
    	led_cdev->brightness_set = led_avs_brightness_set;
    	led_cdev->flags |= LED_DEV_CAP_AVS_UX; /* Specify AVS lighting effect */
    
    	ret = led_classdev_avsux_register(&i2c->dev, &avs->aucdev);
    	if (ret < 0) {
    		dev_err(&i2c->dev, "Register avsux led failed:%d\n", ret);
    		return ret;
    	}
    
    	i2c_set_clientdata(i2c, avs);
    
    	return 0;
    }
    
  • Things to note:

    • There are currently three types of LEDs.

      | Type | Description |
      | ----|----|
      | AVS_UX_TYPE_SINGLE | Single light |
      | AVS_UX_TYPE_RADIAL | Ring light |
      | AVS_UX_TYPE_LINEAR | Line light |
      
      • The driver framework supports up to 12 LEDs by default. If you need to support more LEDs, please modify the macro definition in the header file led-class-avsux.h
      #define MAX_NUM_LEDS 12
      

Example

Tuya provides a general-purpose driver for single-lamp arrays driven by PWM or GPIO.

Copy the attached linux/drivers/leds/leds-pwm-avsux.c to the corresponding directory of the kernel, and modify the Makefile.

Then enable pwm in the device tree, and add the following nodes, pwm is modified according to the actual situation:

pwmleds {
	compatible = "avs-pwm-leds";
	status = "okay";
	avs-red {
		label = "red";
		pwms = <&pwm0 0 10000000 0>;
		max-brightness = <255>;
	};
	avs-green {
		label = "green";
		pwms = <&pwm1 0 10000000 0>;
		max-brightness = <255>;
	};
	avs-blue {
		label = "blue";
		pwms = <&pwm2 0 10000000 0>;
		max-brightness = <255>;
	};
};

If you are using gpio to simulate pwm, copy the attached linux/drivers/pwm/pwm-gpio.c to the corresponding directory of the kernel, and modify the Makefile. Then add the following content to the device tree, and pwm should be modified according to the actual situation:

pwm0: pwm-gpio-r {
	compatible = "pwm-gpio";
	#pwm-cells = <3>;
	pwm-gpios = <&gpio1 RK_PA4 GPIO_ACTIVE_HIGH>;
};
pwm1: pwm-gpio-g {
	compatible = "pwm-gpio";
	#pwm-cells = <3>;
	pwm-gpios = <&gpio1 RK_PA4 GPIO_ACTIVE_HIGH>;
};
pwm2: pwm-gpio-b {
	compatible = "pwm-gpio";
	#pwm-cells = <3>;
	pwm-gpios = <&gpio1 RK_PA4 GPIO_ACTIVE_HIGH>;
};

In addition, Tuya provides the driver of aw20036 in the attachment, you can refer to and transplant it.

Test lighting effect driver

After the driver is compiled and loaded, the following two attribute nodes will be generated under sysfs:

  • /sys/class/leds/avs-pwm-led/avsux_animation
  • /sys/class/leds/avs-pwm-led/avsux_select

First prepare a lighting effect file test.aniamtaion, the content is as follows:

20:100000,100000,100000,100000,100000,100000,100000,100000,100000,100000,100000,100000
20:200000,200000,200000,200000,200000,200000,200000,200000,200000,200000,200000,200000
20:300000,300000,300000,300000,300000,300000,300000,300000,300000,300000,300000,300000
20:400000,400000,400000,400000,400000,400000,400000,400000,400000,400000,400000,400000
20:500000,500000,500000,500000,500000,500000,500000,500000,500000,500000,500000,500000
20:600000,600000,600000,600000,600000,600000,600000,600000,600000,600000,600000,600000
20:700000,700000,700000,700000,700000,700000,700000,700000,700000,700000,700000,700000
20:800000,800000,800000,800000,800000,800000,800000,800000,800000,800000,800000,800000
20:900000,900000,900000,900000,900000,900000,900000,900000,900000,900000,900000,900000
20:A00000,A00000,A00000,A00000,A00000,A00000,A00000,A00000,A00000,A00000,A00000,A00000
20:B00000,B00000,B00000,B00000,B00000,B00000,B00000,B00000,B00000,B00000,B00000,B00000
20:C00000,C00000,C00000,C00000,C00000,C00000,C00000,C00000,C00000,C00000,C00000,C00000

Copy the file to the /lib/firmware/ directory of the target board.

Load the lighting effect to the driver:

/ # echo "test test.animation"> /sys/class/leds/avs-pwm-led/avsux_animation

Select and enable lighting effects:

/ # echo "test"> /sys/class/leds/avs-pwm-led/avsux_select

Now, you can see the red light flickering from dark to bright.

AVS lighting effect animation file

The .animation file describes a complete lighting effect animation, in which each line is a frame and corresponds to a pattern. The specific format is as follows:

Among them, duration represents the display duration of this frame (in milliseconds).
HEX_XX is the hexadecimal RGB value of the corresponding lamp, and each lamp is separated by ,. It should be noted that values ​​greater than the number of LEDs will be ignored by the driver.

All lighting effects used by AVS have been defined in the attached led_animation directory. Currently, single lights, ring lights and line lights are supported. Customers can modify or add new lighting effects as needed.

LED HAL

In addition to the AVSUX Class driver framework, Tuya also provides the libLedAnimation library to implement more complex LED logic and state machine state management.

The provided API is as follows:

/**
* LedAnimationInit()-Lighting effect initialization function
* @conf: The path of the lighting effect configuration file, when it is NULL, it will be loaded from /etc/led_config.json by default
*/
ledHandle_t LedAnimationInit(const char *conf);

/**
* LedAnimationUpdate()-Lighting effect update function
* @handle: Handle returned by LedAnimationInit()
* @animation: Animation name defined in the configuration file
*/
int LedAnimationUpdate(ledHandle_t* handle, const char* animation);
int LedAnimationRelease(ledHandle_t* handle);

The syntax of the lighting effect configuration file /etc/led_config.json is as follows:

device Description
device sysfs path of LED driver
type Type of LED
animations Supported lighting effects

And each lighting effect contains the following attributes:

Properties Description
fw_path Light effect animation file path (relative path under /lib/firmware).
priority Lighting effect priority. High-priority lighting effects can interrupt the coverage of low-priority lighting effects.
loop The number of loops of the lighting effect animation. 0 means infinite loop.
pause Pause time between two loops.
{
	"device": "/sys/class/leds/avs-pwm-led",
	"type": "single"
	"animations": {
	"idle": {
		"fw_path": "led_animation/single/idle.animation",
		"priority": "1",
		"loop": "0",
		"pause":"0"
	},
	"bootup": {
		"fw_path": "led_animation/single/bootup.animation",
		"priority": "0",
		"loop": "0",
		"pause":"0"
	},
	"listening_start": {
		"fw_path": "led_animation/single/active-waking.animation",
		"priority": "0",
		"loop": "0",
		"pause":"0"
	},
......