diff --git a/app/drivers/led/CMakeLists.txt b/app/drivers/led/CMakeLists.txt new file mode 100644 index 00000000..b2879ffe --- /dev/null +++ b/app/drivers/led/CMakeLists.txt @@ -0,0 +1,4 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +add_subdirectory_ifdef(CONFIG_IS31FL3733 is31fl3733) diff --git a/app/drivers/led/Kconfig b/app/drivers/led/Kconfig new file mode 100644 index 00000000..b5c5ed23 --- /dev/null +++ b/app/drivers/led/Kconfig @@ -0,0 +1,4 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +rsource "is31fl3733/Kconfig" diff --git a/app/drivers/led/is31fl3733/CMakeLists.txt b/app/drivers/led/is31fl3733/CMakeLists.txt new file mode 100644 index 00000000..fbb5813f --- /dev/null +++ b/app/drivers/led/is31fl3733/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +zephyr_library() + +zephyr_library_sources(is31fl3733.c) diff --git a/app/drivers/led/is31fl3733/Kconfig b/app/drivers/led/is31fl3733/Kconfig new file mode 100644 index 00000000..390a0832 --- /dev/null +++ b/app/drivers/led/is31fl3733/Kconfig @@ -0,0 +1,20 @@ +# Copyright (c) 2020 Cameron Banna + +config IS31FL3733 + bool "IS31FL3733 LED driver" + depends on I2C + help + Enable LED driver for IS31FL3733. + + The IS31FL3733 is a general purpose 12 by 16 LED + matrix driver with 1/12 cycle rate. The device can be + programmed via an I2C compatible interface. Each + LED can be dimmed individually with 8-bit PWM data + which allowing 256 steps of linear dimming + +config LED_INIT_PRIORITY + int "LED initialization priority" + default 90 + help + System initialization priority for LED drivers. + \ No newline at end of file diff --git a/app/drivers/led/is31fl3733/is31fl3733.c b/app/drivers/led/is31fl3733/is31fl3733.c new file mode 100644 index 00000000..180e94e0 --- /dev/null +++ b/app/drivers/led/is31fl3733/is31fl3733.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2020 Cameron Banna. + */ + +#define DT_DRV_COMPAT issi_is31fl3733 + +/* +* "SUMMARY" +*/ + +#include +#include +#include +#include + +#define LOG_LEVEL CONFIG_LED_LOG_LEVEL +#include +LOG_MODULE_REGISTER(is31fl3733); + /** Number of CS lines. */ +#define IS31FL3733_CS (16) + /** Number of SW lines. */ +#define IS31FL3733_SW (12) + /** IS31FL3733 common registers. */ +#define IS31FL3733_PSR (0xFD) ///< Page select register. Write only. +#define IS31FL3733_PSWL (0xFE) ///< Page select register write lock. Read/Write. + /** Registers in Page 0. */ +#define IS31FL3733_LEDONOFF (0x0000) /// ON or OFF state control for each LED. Write only. +#define IS31FL3733_LEDOPEN (0x0018) /// Open state for each LED. Read only. +#define IS31FL3733_LEDSHORT (0x0030) /// Short state for each LED. Read only. + /** Registers in Page 1. */ +#define IS31FL3733_LEDPWM (0x0100) /// PWM duty for each LED. Write only. + /** Registers in Page 3. */ +#define IS31FL3733_CR (0x0300) /// Configuration Register. Write only. +#define IS31FL3733_RESET (0x0311) /// Reset register. Read only. + /** PSWL register bits. */ +#define IS31FL3733_PSWL_DISABLE (0x00) /// Disable write to Page Select register. +#define IS31FL3733_PSWL_ENABLE (0xC5) /// Enable write to Page select register. + +struct is31fl3733_config { + int reg; + int inst; + char *bus_name; +}; +struct is31fl3733_data { + const struct device *i2c; +}; +uint8_t leds[IS31FL3733_SW * IS31FL3733_CS / 8]; + +static int is31fl3733_set_page(const struct device *dev, uint16_t addr){ + const struct is31fl3733_data *dev_data = dev->data; + const struct is31fl3733_config *dev_cfg = dev->config; + if (i2c_reg_write_byte(dev_data->i2c, dev_cfg->reg, IS31FL3733_PSWL, IS31FL3733_PSWL_ENABLE)){ + LOG_ERR("Enable write to Page select register failed"); + return -EIO; + } + if (i2c_reg_write_byte(dev_data->i2c, dev_cfg->reg, IS31FL3733_PSR, addr)){ + LOG_ERR("Writing to Page select register failed"); + return -EIO; + } + return 0; +} +static int is31fl3733_write_page_reg(const struct device *dev, uint8_t reg, uint8_t buffer){ + const struct is31fl3733_data *dev_data = dev->data; + const struct is31fl3733_config *dev_cfg = dev->config; + if (i2c_reg_write_byte(dev_data->i2c, dev_cfg->reg, reg, buffer)){ + LOG_ERR("Writing Page Failed"); + return -EIO; + } + return 0; +} +static int is31fl3733_led_set_brightness(const struct device *dev, uint32_t led, uint8_t value){ + uint8_t cs = led - ((led / 16) * 16); + uint8_t sw = led / 16; + uint8_t offset = sw * IS31FL3733_CS + cs; + // Set page to 0x01 PWM Page + is31fl3733_set_page(dev, 0x01); + is31fl3733_write_page_reg(dev, offset, value); + return 0; +} +static inline int is31fl3733_led_on(const struct device *dev, uint32_t led){ + uint8_t cs = led - ((led / 16) * 16); + uint8_t sw = led / 16; + uint8_t offset = (sw << 1) + (cs / 8); + leds[offset] |= 0x01 << (cs % 8); + // Set page to 0x01 Led Control Page + is31fl3733_set_page(dev, 0x00); + is31fl3733_write_page_reg(dev, offset, leds[offset]); + return 0; +} +static inline int is31fl3733_led_off(const struct device *dev, uint32_t led){ + uint8_t cs = led - ((led / 16) * 16); + uint8_t sw = led / 16; + uint8_t offset = (sw << 1) + (cs / 8); + leds[offset] &= ~(0x01 << (cs % 8)); + // Set page to 0x01 Led Control Page + is31fl3733_set_page(dev, 0x00); + is31fl3733_write_page_reg(dev, offset, leds[offset]); + return 0; +} +static int is31fl3733_led_set_color(const struct device *dev,uint32_t led, uint8_t num_of_colors, const uint8_t *colors){ + uint8_t cs = led - ((led / 16) * 16); + uint8_t sw = (led / 16) * 3; + uint8_t offset_red = sw * IS31FL3733_CS + cs; + uint8_t offset_green = (sw + 1) * IS31FL3733_CS + cs; + uint8_t offset_blue = (sw + 2) * IS31FL3733_CS + cs; + // Set page to 0x01 PWM Page + is31fl3733_set_page(dev, 0x01); + is31fl3733_write_page_reg(dev, offset_red, colors[0]); + is31fl3733_write_page_reg(dev, offset_green, colors[1]); + is31fl3733_write_page_reg(dev, offset_blue, colors[2]); + return 0; +} +static int is31fl3733_led_reset(const struct device *dev){ + const struct is31fl3733_data *data = dev->data; + const struct is31fl3733_config *dev_cfg = dev->config; + if (i2c_reg_write_byte(data->i2c, dev_cfg->reg, IS31FL3733_PSWL, IS31FL3733_PSWL_ENABLE)){ + LOG_ERR("Enable write to Page select register failed"); + return -EIO; + } + uint8_t partA = (uint8_t)((IS31FL3733_RESET & 0xFF00) >> 8); + uint8_t partB = (uint8_t)(IS31FL3733_RESET & 0x00FF); + uint8_t tx_buf[2] = {IS31FL3733_PSR, partA}; + // Sequence to reset the IC + if (i2c_write(data->i2c,tx_buf, 2, dev_cfg->reg)){ + LOG_ERR("Reseting Device Failed"); + return -EIO; + } + uint8_t tx_buf2[1] = {partB}; + if (i2c_write(data->i2c, tx_buf2, 1, dev_cfg->reg)){ + LOG_ERR("Reseting Device Failed"); + return -EIO; + } + if (i2c_read(data->i2c, NULL, 1, dev_cfg->reg)){ + LOG_ERR("Reseting Device Failed"); + return -EIO; + } + // Set Page to 0x03 Function page + is31fl3733_set_page(dev, 0x03); + // use the write page reg function to set the config + // register to 0x00 and set the value to 0x01 for normal + // operation, this can also be set to 0x01 for software + // shutdown mode. see page 17 & 18 of datasheet + is31fl3733_write_page_reg(dev, 0x00, 0x01); + return 0; +} + +static int is31fl3733_led_init(const struct device *dev){ + const struct is31fl3733_config *dev_cfg = dev->config; + struct is31fl3733_data *dev_data = dev->data; + dev_data->i2c = device_get_binding(dev_cfg->bus_name); + if (dev_data->i2c == NULL) { + LOG_DBG("Failed to get I2C device"); + return -EINVAL; + } + is31fl3733_led_reset(dev); + // Set the Global Current Control, would like to pull this + // value from the device tree eventually + is31fl3733_write_page_reg(dev, 0x03, 0x01); + uint8_t GCC = 255; + is31fl3733_write_page_reg(dev, 0x01, GCC); + return 0; +} + +static const struct led_driver_api is31fl3733_led_api = { + .on = is31fl3733_led_on, + .off = is31fl3733_led_off, + .set_brightness = is31fl3733_led_set_brightness, + .set_color = is31fl3733_led_set_color, +}; + + + +#define IS31FL3733_INIT(inst) \ + static struct is31fl3733_data is31fl3733_led_data_##inst; \ + static const struct is31fl3733_config is31fl3733_config_##inst = { \ + .bus_name = DT_INST_BUS_LABEL(inst), \ + .reg = DT_INST_REG_ADDR(inst), \ + }; \ + DEVICE_AND_API_INIT(is31fl3733_led##inst, DT_INST_LABEL(inst), \ + &is31fl3733_led_init, &is31fl3733_led_data_##inst, \ + &is31fl3733_config_##inst, POST_KERNEL, CONFIG_LED_INIT_PRIORITY, \ + &is31fl3733_led_api); +DT_INST_FOREACH_STATUS_OKAY(IS31FL3733_INIT) diff --git a/app/drivers/zephyr/dts/bindings/led/is31fl3733/issi,is31fl3733.yaml b/app/drivers/zephyr/dts/bindings/led/is31fl3733/issi,is31fl3733.yaml new file mode 100644 index 00000000..b8427da2 --- /dev/null +++ b/app/drivers/zephyr/dts/bindings/led/is31fl3733/issi,is31fl3733.yaml @@ -0,0 +1,9 @@ +description: | + Driver for the is31fl3733 LED driver + +compatible: "issi,is31fl3733" + +properties: + label: + required: true + type: string \ No newline at end of file