diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 0c45a2623e..3163dd5aa9 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -83,6 +83,55 @@ config RGBLED_LIGHTNESS_CORRECTION correction which helps the RGB LED visually fade and blend in a more linear, smooth, and visually appealing fashion. +config LP503X + + bool "LP503X I2C LED Driver" + default n + select I2C + ---help--- + Enable support for the TI LP503X LED driver which can be + utilized to drive up to 36 (12 RGB) LEDs. + +if LP503X + +config LP503X_LOG_MODE + bool "Enable logarithmic mode (y) or linear mode (n)" + default y + ---help--- + Determines whether brightness scale is logarithmic or linear + +config LP503X_POWER_SAVE + bool "Enable power save mode" + default y + ---help--- + Enable/disable automatic power saving mode + +config LP503X_DITHER_MODE + bool "Enable PWM dithering" + default y + ---help--- + Enable/disable PWM dithering mode + +config LP503X_MAX_CURRENT + bool "Enable Max current option (25.5mA (n), or 35mA (y))" + default n + ---help--- + Sets maximum output current to 25.5mA (default) or 35mA + +config LP503X_GLOBAL_SHUTDOWN + bool "Enable global LED shutdown" + default n + ---help--- + When enabled (y), all LEDs will be turned off/disabled until re-enabled + +config DEBUG_LP503X + bool "Enable debug support for the LP503X" + default n + ---help--- + Enables debug support for the LP503X + +endif # LP503X + config PCA9635PW bool "PCA9635PW I2C LED Driver" default n diff --git a/drivers/leds/Make.defs b/drivers/leds/Make.defs index 9f2c0bc701..480a95690b 100644 --- a/drivers/leds/Make.defs +++ b/drivers/leds/Make.defs @@ -47,6 +47,12 @@ ifeq ($(CONFIG_RGBLED),y) LEDVPATH = :leds endif +ifeq ($(CONFIG_LP503X),y) + CSRCS += lp503x.c + LEDDEPATH = --dep-path leds + LEDVPATH = :leds +endif + ifeq ($(CONFIG_PCA9635PW),y) CSRCS += pca9635pw.c LEDDEPATH = --dep-path leds diff --git a/drivers/leds/lp503x.c b/drivers/leds/lp503x.c new file mode 100644 index 0000000000..fdd1277ce9 --- /dev/null +++ b/drivers/leds/lp503x.c @@ -0,0 +1,969 @@ +/**************************************************************************** + * drivers/leds/lp503x.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#if defined(CONFIG_I2C) && defined(CONFIG_LP503X) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_LP503X +# define lp503x_err(x, ...) _err(x, ##__VA_ARGS__) +# define lp503x_info(x, ...) _info(x, ##__VA_ARGS__) +#else +# define lp503x_err(x, ...) uerr(x, ##__VA_ARGS__) +# define lp503x_info(x, ...) uinfo(x, ##__VA_ARGS__) +#endif + +/**************************************************************************** + * Private Type Definitions + ****************************************************************************/ + +enum lp503x_state +{ + LP503X_STATE_UNINIT = 0, + LP503X_STATE_RESET, + LP503X_STATE_CONFIGURED, +}; + +struct lp503x_dev_s +{ + struct i2c_master_s *i2c; + uint8_t i2c_addr; + int i2c_freq; + int count; + + /* device configuration/setup data */ + + struct lp503x_config_s *lp503x_config; + + /* current state of the lp503x device */ + + enum lp503x_state state; +}; + +/* A set of default config parameters as set in LED driver Kconfig */ + +struct lp503x_config_s config_default = +{ +#ifdef CONFIG_LP503X_LOG_MODE + .enable_log_mode = 1, +#else + .enable_log_mode = 0, +#endif +#ifdef CONFIG_LP503X_POWER_SAVE + .enable_power_save = 1, +#else + .enable_power_save = 0, +#endif +#ifdef CONFIG_LP503X_DITHER_MODE + .enable_pwm_dithering = 1, +#else + .enable_pwm_dithering = 0, +#endif +#ifdef CONFIG_LP503X_MAX_CURRENT + .set_max_current_35ma = 1, +#else + .set_max_current_35ma = 0, +#endif +#ifdef CONFIG_LP503X_GLOBAL_SHUTDOWN + .enable_all_led_shutdown = 1, +#else + .enable_all_led_shutdown = 0, +#endif + + /* all leds will default to independent control, not bank control */ + + .led_mode[0] = LP503X_LED_BANK_MODE_DISABLED, + .led_mode[1] = LP503X_LED_BANK_MODE_DISABLED, + .led_mode[2] = LP503X_LED_BANK_MODE_DISABLED, + .led_mode[3] = LP503X_LED_BANK_MODE_DISABLED, + .led_mode[4] = LP503X_LED_BANK_MODE_DISABLED, + .led_mode[5] = LP503X_LED_BANK_MODE_DISABLED, + .led_mode[6] = LP503X_LED_BANK_MODE_DISABLED, + .led_mode[7] = LP503X_LED_BANK_MODE_DISABLED, + .led_mode[8] = LP503X_LED_BANK_MODE_DISABLED, + .led_mode[9] = LP503X_LED_BANK_MODE_DISABLED, + .led_mode[10] = LP503X_LED_BANK_MODE_DISABLED, + .led_mode[11] = LP503X_LED_BANK_MODE_DISABLED, +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int lp503x_i2c_write_reg(struct lp503x_dev_s *priv, + uint8_t const reg_addr, + uint8_t const reg_val); +static int lp503x_i2c_read_reg(struct lp503x_dev_s *priv, + uint8_t const reg_addr, + uint8_t *regval); +static int lp503x_open(struct file *filep); +static int lp503x_close(struct file *filep); +static int lp503x_ioctl(struct file *filep, int cmd, + unsigned long arg); +#ifdef CONFIG_DEBUG_LP503X +static int lp503x_dump_registers(struct lp503x_dev_s *priv, + const char *msg); +#else +# define lp503x_dump_registers(priv, msg); +#endif +static int lp503x_reset(struct lp503x_dev_s *priv); +static int lp503x_enable(struct lp503x_dev_s *priv, bool enable); +static int lp503x_set_rgbbrightness(struct lp503x_dev_s *priv, int led, + int brightness); +static int lp503x_set_outcolour(struct lp503x_dev_s *priv, int led, + int colour); +static int lp503x_set_config(struct lp503x_dev_s *priv); +static int lp503x_set_bank_mode(struct lp503x_dev_s *priv); +static int lp503x_set_bank_colour(struct lp503x_dev_s *priv, char bank, + int brightness); +static int lp503x_set_bank_brightness(struct lp503x_dev_s *priv, + int brightness); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_lp503x_fileops = +{ + lp503x_open, /* open */ + lp503x_close, /* close */ + NULL, /* read */ + NULL, /* write */ + NULL, /* seek */ + lp503x_ioctl, /* ioctl */ + NULL /* poll */ +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , NULL /* unlink */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lp503x_dumpregs + * + * Description: + * Dump the contents of all lp503x registers + * + * Input Parameters: + * priv - A reference to the lp503x peripheral state + * msg - Message to print before the register data + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_LP503X +static int lp503x_dump_registers(struct lp503x_dev_s *priv, + const char *msg) +{ + uint8_t val1; + uint8_t val2; + uint8_t val3; + uint8_t val4; + int ret; + lp503x_info("lp503x Registers: %s\n", msg); + ret = lp503x_i2c_read_reg(priv, LP503X_DEVICE_CONFIG0, &val1); + ret = lp503x_i2c_read_reg(priv, LP503X_DEVICE_CONFIG1, &val2); + ret = lp503x_i2c_read_reg(priv, LP503X_LED_CONFIG0, &val3); + ret = lp503x_i2c_read_reg(priv, LP503X_LED_CONFIG1, &val4); + lp503x_info + ("Dev Config0:\t%02x Dev Conf1:\t%02x \ + LED Conf0:\t%02x: LED Conf1: \t %02x\n", + val1, val2, val3, val4); + + ret = lp503x_i2c_read_reg(priv, LP503X_BANK_BRIGHTNESS, &val1); + ret = lp503x_i2c_read_reg(priv, LP503X_BANK_A_COLOUR, &val2); + ret = lp503x_i2c_read_reg(priv, LP503X_BANK_B_COLOUR, &val3); + ret = lp503x_i2c_read_reg(priv, LP503X_BANK_C_COLOUR, &val4); + lp503x_info + ("Bank Bright:\t%02x BankA Col:\t%02x \ + BankB Col:\t%02x: BankC Col:\t %02x\n", + val1, val2, val3, val4); + + ret = lp503x_i2c_read_reg(priv, LP503X_LED0_BRIGHTNESS, &val1); + ret = lp503x_i2c_read_reg(priv, LP503X_LED1_BRIGHTNESS, &val2); + ret = lp503x_i2c_read_reg(priv, LP503X_LED2_BRIGHTNESS, &val3); + ret = lp503x_i2c_read_reg(priv, LP503X_LED3_BRIGHTNESS, &val4); + lp503x_info + ("LED0 Bright:\t%02x LED1 Col:\t%02x \ + LED2 Bright:\t%02x: LED3 Bright: %02x\n", + val1, val2, val3, val4); + + ret = lp503x_i2c_read_reg(priv, LP503X_LED4_BRIGHTNESS, &val1); + ret = lp503x_i2c_read_reg(priv, LP503X_LED5_BRIGHTNESS, &val2); + ret = lp503x_i2c_read_reg(priv, LP503X_LED6_BRIGHTNESS, &val3); + ret = lp503x_i2c_read_reg(priv, LP503X_LED7_BRIGHTNESS, &val4); + lp503x_info + ("LED4 Bright:\t%02x LED5 Bright:\t%02x \ + LED6 Bright:\t%02x: LED7 Bright: %02x\n", + val1, val2, val3, val4); + + ret = lp503x_i2c_read_reg(priv, LP503X_LED8_BRIGHTNESS, &val1); + ret = lp503x_i2c_read_reg(priv, LP503X_LED9_BRIGHTNESS, &val2); + ret = lp503x_i2c_read_reg(priv, LP503X_LED10_BRIGHTNESS, &val3); + ret = lp503x_i2c_read_reg(priv, LP503X_LED11_BRIGHTNESS, &val4); + lp503x_info + ("LED8 Bright:\t%02x LED9 Bright:\t%02x \ + LED10 Bright:\t%02x: LED11 Bright:%02x\n", + val1, val2, val3, val4); + + ret = lp503x_i2c_read_reg(priv, LP503X_OUT0_COLOUR, &val1); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT1_COLOUR, &val2); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT2_COLOUR, &val3); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT3_COLOUR, &val4); + lp503x_info + ("Out0 Col:\t%02x Out1 Col:\t%02x \ + Out2 Col:\t\t%02x Out3 Col:\t %02x\n", + val1, val2, val3, val4); + + ret = lp503x_i2c_read_reg(priv, LP503X_OUT4_COLOUR, &val1); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT5_COLOUR, &val2); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT6_COLOUR, &val3); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT7_COLOUR, &val4); + lp503x_info + ("Out4 Col:\t%02x Out5 Col:\t%02x \ + Out6 Col:\t\t%02x Out7 Col:\t %02x\n", + val1, val2, val3, val4); + + ret = lp503x_i2c_read_reg(priv, LP503X_OUT8_COLOUR, &val1); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT9_COLOUR, &val2); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT10_COLOUR, &val3); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT11_COLOUR, &val4); + lp503x_info + ("Out8 Col:\t%02x Out9 Col:\t%02x \ + Out10 Col:\t%02x Out11 Col:\t %02x\n", + val1, val2, val3, val4); + + ret = lp503x_i2c_read_reg(priv, LP503X_OUT12_COLOUR, &val1); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT13_COLOUR, &val2); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT14_COLOUR, &val3); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT15_COLOUR, &val4); + lp503x_info + ("Out12 Col:\t%02x Out13 Col:\t%02x \ + Out14 Col:\t%02x Out15 Col:\t %02x\n", + val1, val2, val3, val4); + + ret = lp503x_i2c_read_reg(priv, LP503X_OUT16_COLOUR, &val1); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT17_COLOUR, &val2); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT18_COLOUR, &val3); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT19_COLOUR, &val4); + lp503x_info + ("Out16 Col:\t%02x Out17 Col:\t%02x \ + Out18 Col:\t%02x Out19 Col:\t %02x\n", + val1, val2, val3, val4); + + ret = lp503x_i2c_read_reg(priv, LP503X_OUT20_COLOUR, &val1); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT21_COLOUR, &val2); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT22_COLOUR, &val3); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT23_COLOUR, &val4); + lp503x_info + ("Out20 Col:\t%02x Out21 Col:\t%02x \ + Out22 Col:\t%02x Out23 Col:\t %02x\n", + val1, val2, val3, val4); + + ret = lp503x_i2c_read_reg(priv, LP503X_OUT24_COLOUR, &val1); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT25_COLOUR, &val2); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT26_COLOUR, &val3); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT27_COLOUR, &val4); + lp503x_info + ("Out24 Col:\t%02x Out25 Col:\t%02x \ + Out26 Col:\t%02x Out27 Col:\t %02x\n", + val1, val2, val3, val4); + + ret = lp503x_i2c_read_reg(priv, LP503X_OUT28_COLOUR, &val1); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT29_COLOUR, &val2); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT30_COLOUR, &val3); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT31_COLOUR, &val4); + lp503x_info + ("Out28 Col:\t%02x Out29 Col:\t%02x \ + Out30 Col:\t%02x Out31 Col:\t %02x\n", + val1, val2, val3, val4); + + ret = lp503x_i2c_read_reg(priv, LP503X_OUT32_COLOUR, &val1); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT33_COLOUR, &val2); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT34_COLOUR, &val3); + ret = lp503x_i2c_read_reg(priv, LP503X_OUT35_COLOUR, &val4); + lp503x_info + ("Out28 Col:\t%02x Out29 Col:\t%02x \ + Out30 Col:\t%02x Out31 Col:\t %02x\n", + val1, val2, val3, val4); + + return ret; +} + +#endif +/**************************************************************************** + * Name: lp503x_i2c_write_reg + * + * Description: + * Write a single byte to one of the LP503X configuration registers. + * + ****************************************************************************/ + +static int lp503x_i2c_write_reg(struct lp503x_dev_s *priv, + uint8_t const reg_addr, + uint8_t const reg_val) +{ + struct i2c_config_s config; + int ret; + + /* assemble the 2 byte message comprised of reg_addr and reg_val */ + + uint8_t const BUFFER_SIZE = 2; + uint8_t buffer[BUFFER_SIZE]; + + buffer[0] = reg_addr; + buffer[1] = reg_val; + + /* Setup up the I2C configuration */ + + config.frequency = priv->i2c_freq; + config.address = priv->i2c_addr; + config.addrlen = 7; + + /* Write the register address followed by the data (no RESTART) */ + + ledinfo("i2c addr: 0x%02X reg addr: 0x%02X value: 0x%02X\n", + priv->i2c_addr, buffer[0], buffer[1]); + + ret = i2c_write(priv->i2c, &config, buffer, BUFFER_SIZE); + if (ret < 0) + { + lederr("ERROR: i2c_write returned error code %d\n", ret); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Name: lp503x_i2c_read_reg + * + * Description: + * Read a single byte from one of the LP503X configuration registers. + * + ****************************************************************************/ + +static int lp503x_i2c_read_reg(struct lp503x_dev_s *priv, + uint8_t const reg_addr, + uint8_t *regval) +{ + struct i2c_config_s config; + int ret; + + /* Setup up the I2C configuration */ + + config.frequency = priv->i2c_freq; + config.address = priv->i2c_addr; + config.addrlen = 7; + + /* Write the register address followed by the data (no RESTART) */ + + ret = i2c_write(priv->i2c, &config, ®_addr, 1); + ret = i2c_read(priv->i2c, &config, regval, 1); + + return ret; +} + +/**************************************************************************** + * Name: lp503x_open + * + * Description: + * This function is called whenever a LP503X device is opened. + * + ****************************************************************************/ + +static int lp503x_open(struct file *filep) +{ + struct inode *inode = filep->f_inode; + struct lp503x_dev_s *priv = inode->i_private; + int ret; + + ledinfo("INFO: Opening, resetting and enabling the LP503X for business\n"); + + /* reset and enable the device */ + + /* means the device was possibly never regsitered? */ + + if (priv->state == LP503X_STATE_UNINIT) + { + return -ENODEV; + } + else if (priv->state == LP503X_STATE_RESET) + { + ret = lp503x_enable(priv, true); + + if (ret != 0) + { + lederr("ERROR: unable to enable lp503x\n"); + return -EIO; + } + else + { + /* use device defaults */ + + priv->lp503x_config = &config_default; + } + + ret = lp503x_set_config(priv); + if (ret != 0) + { + lederr("ERROR: Unable to set device config: %d\n", ret); + return -EIO; + } + + priv->state = LP503X_STATE_CONFIGURED; + } + + lp503x_dump_registers(priv, "File Open"); + + return ret; +} + +/**************************************************************************** + * Name: lp503x_close + * + * Description: + * This function is called whenever a LP503X device is closed. + * + ****************************************************************************/ + +static int lp503x_close(struct file *filep) +{ + int ret; + + struct inode *inode = filep->f_inode; + struct lp503x_dev_s *priv = inode->i_private; + + ret = lp503x_enable(priv, false); + if (ret < 0) + { + lederr("ERROR: Could not disable LP503X\n"); + } + + return ret; +} + +/**************************************************************************** + * Name: lp503x_reset + * + * Description: + * Resets all registers to default values + * + ****************************************************************************/ + +static int lp503x_reset(struct lp503x_dev_s *priv) +{ + int ret; + + ret = lp503x_i2c_write_reg(priv, LP503X_RESET, LP503X_RESET_ALL_REGISTERS); + if (ret != 0) + { + return -EIO; + } + else + { + priv->state = LP503X_STATE_RESET; + return OK; + } +} + +/**************************************************************************** + * Name: lp503x_enable + * + * Description: + * enables or disables the entire device + * + ****************************************************************************/ + +static int lp503x_enable(struct lp503x_dev_s *priv, bool enable) +{ + int ret; + + if (enable) + { + ret = lp503x_i2c_write_reg(priv, LP503X_DEVICE_CONFIG0, + LP503X_CHIP_ENABLE); + ledinfo("INFO: LP503x enabled\n"); + } + else + { + ret = lp503x_i2c_write_reg(priv, LP503X_DEVICE_CONFIG0, + LP503X_CHIP_DISABLE); + ledinfo("INFO: LP503x disabled\n"); + } + + return ret; +} + +/**************************************************************************** + * Name: lp503x_set_config + * + * Description: + * configures basic operation modes of the device + * + ****************************************************************************/ + +static int lp503x_set_config(struct lp503x_dev_s *priv) +{ + int ret; + uint8_t regval; + struct lp503x_config_s *config; + + config = priv->lp503x_config; + + ret = lp503x_i2c_read_reg(priv, LP503X_DEVICE_CONFIG1, ®val); + if (config->enable_log_mode) + { + regval |= LP503X_CONFIG1_LOG_SCALE; + } + else + { + regval &= ~LP503X_CONFIG1_LOG_SCALE; + } + + if (config->enable_power_save) + { + regval |= LP503X_CONFIG1_PWRSAVE; + } + else + { + regval &= ~LP503X_CONFIG1_PWRSAVE; + } + + if (config->enable_pwm_dithering) + { + regval |= LP503X_CONFIG1_DITHERING; + } + else + { + regval &= ~LP503X_CONFIG1_DITHERING; + } + + if (config->set_max_current_35ma) + { + regval |= LP503X_CONFIG1_PWRSAVE; + } + else + { + regval &= ~LP503X_CONFIG1_PWRSAVE; + } + + if (config->enable_all_led_shutdown) + { + regval |= LP503X_CONFIG1_GLOBAL_OFF; + } + else + { + regval &= ~LP503X_CONFIG1_GLOBAL_OFF; + } + + ret = lp503x_i2c_write_reg(priv, LP503X_DEVICE_CONFIG1, regval); + + return ret; +} + +/**************************************************************************** + * Name: lp503x_set_bank_brightness + * + * Description: + * sets banks to the required brightness + * + ****************************************************************************/ + +static int lp503x_set_bank_brightness(struct lp503x_dev_s *priv, + int brightness) +{ + if (brightness > MAX_BRIGHTNESS) + { + return -EINVAL; + } + else + { + return lp503x_i2c_write_reg(priv, LP503X_BANK_BRIGHTNESS, brightness); + } +} + +/**************************************************************************** + * Name: lp503x_set_bank_colour + * + * Description: + * sets bank A, B or C led to required coloiur (mix) + * + ****************************************************************************/ + +static int lp503x_set_bank_colour(struct lp503x_dev_s *priv, char bank, + int brightness) +{ + if (brightness > MAX_BRIGHTNESS) + { + return -EINVAL; + } + else + { + if (bank == 'A') + { + return lp503x_i2c_write_reg(priv, LP503X_BANK_A_COLOUR, + brightness); + } + else if (bank == 'B') + { + return lp503x_i2c_write_reg(priv, LP503X_BANK_B_COLOUR, + brightness); + } + else if (bank == 'C') + { + return lp503x_i2c_write_reg(priv, LP503X_BANK_C_COLOUR, + brightness); + } + else + { + return -EINVAL; + } + } +} + +/**************************************************************************** + * Name: lp503x_set_bank_mode + * + * Description: + * enables or disables bank mode for selected LED + * + ****************************************************************************/ + +static int lp503x_set_bank_mode(struct lp503x_dev_s *priv) +{ + int ret; + int count; + int regval; + + struct lp503x_config_s *config; + + config = priv->lp503x_config; + + regval = 0; + for (count = 0; count < 8; count++) + { + if (config->led_mode[count] == LP503X_LED_BANK_MODE_ENABLED) + { + regval |= (LP503X_LED0_BANK_ENABLE << count); + } + else + { + regval &= ~(LP503X_LED0_BANK_ENABLE << count); + } + } + + ret = lp503x_i2c_write_reg(priv, LP503X_LED_CONFIG0, regval); + + for (count = 8; count < 12; count++) + { + if (config->led_mode[count] == LP503X_LED_BANK_MODE_ENABLED) + { + regval |= (LP503X_LED0_BANK_ENABLE << (count - 8)); + } + else + { + regval &= ~(LP503X_LED0_BANK_ENABLE << (count - 8)); + } + } + + ret = lp503x_i2c_write_reg(priv, LP503X_LED_CONFIG1, regval); + + return ret; +} + +/**************************************************************************** + * Name: lp503x_set_rgbled_colour + * + * Description: + * sets RGB led to chosen html colour + * + ****************************************************************************/ + +static int lp503x_set_rgbled_colour(struct lp503x_dev_s *priv, + int led, int colour) +{ + int ret; + int regaddr; + + if ((led > MAX_RGB_LEDS) || (colour > MAX_RGB_COLOUR)) + { + ret = -EINVAL; + } + else + { + regaddr = LP503X_OUT0_COLOUR + (3*led); + + ret = lp503x_i2c_write_reg(priv, regaddr++, (colour >> 16) & 0xff); + ret = lp503x_i2c_write_reg(priv, regaddr++, (colour >> 8) & 0xff); + ret = lp503x_i2c_write_reg(priv, regaddr, (colour >> 0) & 0xff); + ledinfo("INFO: RGB LED %d set to RGB colour %06x\n", led, colour); + } + + return ret; +} + +/**************************************************************************** + * Name: lp503x_set_outcolour + * + * Description: + * Sets OUT brightness ("colour" of individual LED outputs + * + ****************************************************************************/ + +static int lp503x_set_outcolour(struct lp503x_dev_s *priv, int led, + int brightness) +{ + int ret; + if ((led > MAX_LEDS) || (brightness > MAX_BRIGHTNESS)) + { + ret = -EINVAL; + } + else + { + ret = lp503x_i2c_write_reg(priv, LP503X_OUT0_COLOUR + led, brightness); + ledinfo("INFO: individual LED %d set to brightness %d\n", led, + brightness); + } + + return ret; +} + +/**************************************************************************** + * Name: lp503x_set_rgbbrightness + * + * Description: + * Sets brightness of all RGB LED + * + ****************************************************************************/ + +static int lp503x_set_rgbbrightness(struct lp503x_dev_s *priv, int led, + int brightness) +{ + int ret; + + if ((led > MAX_RGB_LEDS) || (brightness > MAX_BRIGHTNESS)) + { + ret = -EINVAL; + } + else + { + ret = lp503x_i2c_write_reg(priv, LP503X_LED0_BRIGHTNESS + led, + brightness); + ledinfo("INFO: LED %d set to brightness %d\n", led, brightness); + } + + return ret; +} + +/**************************************************************************** + * Name: lp503x_ioctl + * + * Description: + * This function is called whenever an ioctl call to a LP503X is + * performed. + * + ****************************************************************************/ + +static int lp503x_ioctl(struct file *filep, int cmd, + unsigned long arg) +{ + struct inode *inode = filep->f_inode; + struct lp503x_dev_s *priv = inode->i_private; + struct lp503x_config_s *config; + int ret; + const struct ioctl_arg_s *lp503x_ioctl_args = (struct ioctl_arg_s *)arg; + + config = priv->lp503x_config; + + ret = OK; + + ledinfo("cmd: %d arg: %ld\n", cmd, arg); + + switch (cmd) + { + case PWMIOC_ENABLE: /* arg is true or false */ + config->enable_all_led_shutdown = lp503x_ioctl_args->param; + ret = lp503x_set_config(priv); + break; + + case PWMIOC_RESET: /* no args */ + lp503x_reset(priv); + break; + + case PWMIOC_ENABLE_LED_BANK_MODE: /* led(0..11), mode required */ + ledinfo("INFO: setting LED %d mode to %" PRIx32 "\n", + lp503x_ioctl_args->lednum, + lp503x_ioctl_args->param); + config->led_mode[lp503x_ioctl_args->lednum] = + lp503x_ioctl_args->param; + lp503x_set_bank_mode(priv); + break; + + case PWMIOC_SET_BANK_MIX_COLOUR:/* bank(A/B/C),level(0-255) */ + ledinfo("INFO: setting bank %c to brightness %" PRIx32 "\n", + lp503x_ioctl_args->lednum, lp503x_ioctl_args->param); + ret = lp503x_set_bank_colour(priv, lp503x_ioctl_args->lednum, + lp503x_ioctl_args->param); + break; + + case PWMIOC_SET_BANK_BRIGHTNESS: + ledinfo("INFO: setting bank brightness to %" PRIx32 "\n", + lp503x_ioctl_args->param); + lp503x_set_bank_brightness(priv, lp503x_ioctl_args->param); + break; + + case PWMIOC_CONFIG: /* config is struct within priv */ + ledinfo("INFO: setting device config to:\n"); + ledinfo("\tlog mode = %d\n", config->enable_log_mode); + ledinfo("\tpower save = %d\n", config->enable_power_save); + ledinfo("\tpwm dithering = %d\n", config->enable_pwm_dithering); + ledinfo("\tmax current = %s\n", (config->set_max_current_35ma) + ? "30mA" : "25.5mA"); + ledinfo("\tall leds shutdown = %d\n", + config->enable_all_led_shutdown); + + ret = lp503x_set_config(priv); + break; + + case PWMIOC_SET_LED_COLOUR: /* led(0..35), Colour(0..255) */ + ledinfo("INFO: set LED %d to colour/brightness %" PRIx32 "\n", + lp503x_ioctl_args->lednum, lp503x_ioctl_args->param); + ret = lp503x_set_outcolour(priv, lp503x_ioctl_args->lednum, + lp503x_ioctl_args->param); + break; + + case PWMIOC_SET_RGB_BRIGHTNESS: /* led(0..11), level(0..255) */ + ledinfo("INFO: requested brightness level %d for led %" PRIx32 "\n", + lp503x_ioctl_args->lednum, lp503x_ioctl_args->param); + ret = lp503x_set_rgbbrightness(priv, lp503x_ioctl_args->lednum, + lp503x_ioctl_args->param); + break; + + case PWMIOC_SET_RGB_COLOUR: /* led(0..11) */ + ledinfo("requested led %d to be RGB colour = %" PRIx32 "\n", + lp503x_ioctl_args->lednum, lp503x_ioctl_args->param); + ret = lp503x_set_rgbled_colour(priv, lp503x_ioctl_args->lednum, + lp503x_ioctl_args->param); + break; + + default: /* The used ioctl command was invalid */ + lederr("ERROR: Unrecognized cmd: %d\n", cmd); + ret = -ENOTTY; + break; + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lp503x_register + * + * Description: + * Register the LP503X device as 'devpath' + * + * Input Parameters: + * devpath - The full path to the driver to register. E.g., "/dev/leddrv0". + * i2c - An instance of the I2C interface to use to communicate + * with the LM92. + * lp503x_i2c_addr + * - The I2C address of the LP503X. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int lp503x_register(const char *devpath, struct i2c_master_s *i2c, + uint8_t const lp503x_i2c_addr, int const i2c_frequency) +{ + int ret; + + /* Sanity check */ + + DEBUGASSERT(devpath != NULL && i2c != NULL); + + /* Initialize the LP503X device structure */ + + struct lp503x_dev_s *priv = + (struct lp503x_dev_s *)kmm_malloc(sizeof(struct lp503x_dev_s)); + + if (priv == NULL) + { + lederr("ERROR: Failed to allocate instance of lp503x_dev_s\n"); + return -ENOMEM; + } + + priv->i2c = i2c; + priv->i2c_addr = lp503x_i2c_addr; + priv->i2c_freq = i2c_frequency; + priv->state = LP503X_STATE_UNINIT; + + /* Register the character driver */ + + ret = register_driver(devpath, &g_lp503x_fileops, 0222, priv); + if (ret != OK) + { + lederr("ERROR: Failed to register driver: %d\n", ret); + kmm_free(priv); + return ret; + } + else + { + ret = lp503x_reset(priv); + if (ret != OK) + { + lederr("ERROR: failed to reset lp503x device\n"); + return ret; + } + + priv->state = LP503X_STATE_RESET; + } + + return OK; +} + +#endif /* CONFIG_I2C && CONFIG_I2C_LP503X */ diff --git a/include/nuttx/leds/lp503x.h b/include/nuttx/leds/lp503x.h new file mode 100644 index 0000000000..48a3eafa67 --- /dev/null +++ b/include/nuttx/leds/lp503x.h @@ -0,0 +1,270 @@ +/**************************************************************************** + * include/nuttx/leds/lp503x.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_LEDS_LP503X_H +#define __INCLUDE_NUTTX_LEDS_LP503X_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/* Configuration + * CONFIG_I2C - Enables support for I2C drivers + * CONFIG_LP503X - Enables support for the LP503X driver + */ + +#if defined(CONFIG_I2C) && defined(CONFIG_LP503X) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* I2C definitions */ + +#define MAX_LEDS 35 +#define MAX_RGB_LEDS 11 +#define MAX_BRIGHTNESS 255 +#define MAX_RGB_COLOUR 0xffffff + +#define LP503X_I2C_BUS_FREQ_HZ (380000) + +/* LP503X Register Map */ + +#define LP503X_DEVICE_CONFIG0 0x00 +#define LP503X_DEVICE_CONFIG1 0x01 + +#define LP503X_LED_CONFIG0 0x02 +#define LP503X_LED_CONFIG1 0x03 + +#define LP503X_BANK_BRIGHTNESS 0x04 + +#define LP503X_BANK_A_COLOUR 0x05 /* All red LEDs */ +#define LP503X_BANK_B_COLOUR 0x06 /* All green LEDs */ +#define LP503X_BANK_C_COLOUR 0x07 /* All blue LEDs */ + +#define LP503X_LED0_BRIGHTNESS 0x08 +#define LP503X_LED1_BRIGHTNESS 0x09 +#define LP503X_LED2_BRIGHTNESS 0x0a +#define LP503X_LED3_BRIGHTNESS 0x0b +#define LP503X_LED4_BRIGHTNESS 0x0c +#define LP503X_LED5_BRIGHTNESS 0x0d +#define LP503X_LED6_BRIGHTNESS 0x0e +#define LP503X_LED7_BRIGHTNESS 0x0f +#define LP503X_LED8_BRIGHTNESS 0x10 +#define LP503X_LED9_BRIGHTNESS 0x11 +#define LP503X_LED10_BRIGHTNESS 0x12 +#define LP503X_LED11_BRIGHTNESS 0x13 + +#define LP503X_OUT0_COLOUR 0x14 +#define LP503X_OUT1_COLOUR 0x15 +#define LP503X_OUT2_COLOUR 0x16 +#define LP503X_OUT3_COLOUR 0x17 +#define LP503X_OUT4_COLOUR 0x18 +#define LP503X_OUT5_COLOUR 0x19 +#define LP503X_OUT6_COLOUR 0x1a +#define LP503X_OUT7_COLOUR 0x1b +#define LP503X_OUT8_COLOUR 0x1c +#define LP503X_OUT9_COLOUR 0x1d +#define LP503X_OUT10_COLOUR 0x1e +#define LP503X_OUT11_COLOUR 0x1f +#define LP503X_OUT12_COLOUR 0x20 +#define LP503X_OUT13_COLOUR 0x21 +#define LP503X_OUT14_COLOUR 0x22 +#define LP503X_OUT15_COLOUR 0x23 +#define LP503X_OUT16_COLOUR 0x24 +#define LP503X_OUT17_COLOUR 0x25 +#define LP503X_OUT18_COLOUR 0x26 +#define LP503X_OUT19_COLOUR 0x27 +#define LP503X_OUT20_COLOUR 0x28 +#define LP503X_OUT21_COLOUR 0x29 +#define LP503X_OUT22_COLOUR 0x2a +#define LP503X_OUT23_COLOUR 0x2b +#define LP503X_OUT24_COLOUR 0x2c +#define LP503X_OUT25_COLOUR 0x2d +#define LP503X_OUT26_COLOUR 0x2e +#define LP503X_OUT27_COLOUR 0x2f +#define LP503X_OUT28_COLOUR 0x30 +#define LP503X_OUT29_COLOUR 0x31 +#define LP503X_OUT30_COLOUR 0x32 +#define LP503X_OUT31_COLOUR 0x33 +#define LP503X_OUT32_COLOUR 0x34 +#define LP503X_OUT33_COLOUR 0x35 +#define LP503X_OUT34_COLOUR 0x36 +#define LP503X_OUT35_COLOUR 0x37 + +#define LP503X_RESET 0x38 + +/* LP503X Commands */ + +/* DEVICE_CONFIG0 */ + +#define LP503X_CHIP_ENABLE (0x01 << 6) +#define LP503X_CHIP_DISABLE 0 + +/* DEVICE_CONFIG1 */ + +#define LP503X_GLOBAL_OFF (0x01 << 0) +#define LP503X_MAX_CURRENT_OPTION (0x01 << 1) +#define LP503X_PWM_DITHERING_ENABLE (0x01 << 2) +#define LP503X_AUTO_INCR_ENABLE (0x01 << 3) +#define LP503X_POWER_SAVE_ENABLE (0x01 << 4) +#define LP503X_LOG_SCALE_ENABLE (0x01 << 5) + +/* LED_CONFIG0 */ + +#define LP503X_LED_BANK_MODE_ENABLED 1 +#define LP503X_LED_BANK_MODE_DISABLED 0 +#define LP503X_LED0_BANK_ENABLE (0x01 << 0) +#define LP503X_LED1_BANK_ENABLE (0x01 << 1) +#define LP503X_LED2_BANK_ENABLE (0x01 << 2) +#define LP503X_LED3_BANK_ENABLE (0x01 << 3) +#define LP503X_LED4_BANK_ENABLE (0x01 << 4) +#define LP503X_LED5_BANK_ENABLE (0x01 << 5) +#define LP503X_LED6_BANK_ENABLE (0x01 << 6) +#define LP503X_LED7_BANK_ENABLE (0x01 << 7) +#define LP503X_ALL_LEDS_BANK_MODE0 0xff + +/* LED_CONFIG1 */ + +#define LP503X_LED8_BANK_ENABLE (0x01 << 0) +#define LP503X_LED9_BANK_ENABLE (0x01 << 1) +#define LP503X_LED10_BANK_ENABLE (0x01 << 2) +#define LP503X_LED11_BANK_ENABLE (0x01 << 3) +#define LP503X_ALL_LEDS_BANK_MODE1 0x0f + +/* RESET ALL REGISTERS */ + +#define LP503X_RESET_ALL_REGISTERS 0xff + +#define LED_RED 0xff0000 +#define LED_GREEN 0x00ff00 +#define LED_BLUE 0x0000ff +#define LED_MAGENTA 0xff00ff +#define LED_YELLOW 0xffff33 +#define LED_CYAN 0x00ffff +#define LED_WHITE 0xffffff +#define LED_ORANGE 0xffa500 + +/* LP503X_CONFIG1 bit definitions, disable=0, enable=1 */ + +#define LP503X_CONFIG1_LOG_SCALE (1 << 5) /* enable log scale not linear */ +#define LP503X_CONFIG1_PWRSAVE (1 << 4) /* power save mode */ +#define LP503X_CONFIG1_AUTOINC (1 << 3) /* register auto increment mode */ +#define LP503X_CONFIG1_DITHERING (1 << 2) /* power saving mode */ +#define LP503X_CONFIG1_MAX_CURRENT (1 << 1) /* responds to I2C subaddr1 */ +#define LP503X_CONFIG1_GLOBAL_OFF (1 << 0) /* enable GLOGAL LEDs off */ + +/* IOCTL commands */ + +#define PWMIOC_ENABLE _PWMIOC(0) /* true/false */ +#define PWMIOC_RESET _PWMIOC(1) /* no args */ +#define PWMIOC_CONFIG _PWMIOC(2) /* lin/log + * pwm dither + * Imax + * Power Save */ +#define PWMIOC_ENABLE_LED_BANK_MODE _PWMIOC(3) /* led(0..11) */ +#define PWMIOC_SET_LED_COLOUR _PWMIOC(4) /* led(0..35), Colour(0..255)*/ +#define PWMIOC_SET_RGB_COLOUR _PWMIOC(5) /* led(0..11), Colour HTML RGB*/ +#define PWMIOC_SET_RGB_BRIGHTNESS _PWMIOC(6) /* led(0..11), level(0..255) */ +#define PWMIOC_SET_BANK_MIX_COLOUR _PWMIOC(7) /* bank(A/B/C),level(0-255) */ +#define PWMIOC_SET_BANK_BRIGHTNESS _PWMIOC(8) /* level(0-255) */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* struct use to set device operating modes + * - log/lin scale + * - power save enable/disable + * - reg auto inc mode (set to FALSE (disable) for this driver + * - pwm dithering enable/disable + * - max current option + * - global LED shutdown control + + */ + +struct lp503x_config_s +{ + bool enable_log_mode; + bool enable_power_save; + bool enable_auto_increment; + bool enable_pwm_dithering; + bool set_max_current_35ma; + bool enable_all_led_shutdown; + bool led_mode[12]; +}; + +/* struct use for most/all ioctl calls */ + +struct ioctl_arg_s +{ + uint8_t lednum; + uint32_t param; +}; + +/**************************************************************************** + * Forward declarations + ****************************************************************************/ + +struct i2c_master_s; + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: lp503x_register + * + * Description: + * Register the LP503X device as 'devpath' + * + * Input Parameters: + * devpath - The full path to the driver to register. E.g., "/dev/leddrv0". + * i2c - An instance of the I2C interface to use to communicate + * with the LM92. + * lp503x_i2c_addr + * - The I2C address of the LP503X. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int lp503x_register(const char *devpath, struct i2c_master_s *i2c, + uint8_t const lp503x_i2c_addr, int const i2c_frequency); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_I2C && CONFIG_I2C_LP503X */ +#endif /* __INCLUDE_NUTTX_LEDS_LP503X_H */