/*
 * Copyright (C) 2020 Freie Universität Berlin
 *
 * This file is subject to the terms and conditions of the GNU Lesser
 * General Public License v2.1. See the file LICENSE in the top level
 * directory for more details.
 */

/**
 * @defgroup    drivers_pca9633 PCA9633 I2C PWM controller
 * @ingroup     drivers_actuators
 * @brief       Device driver for the NXP PCA9633
 *
 * @{
 *
 * @author      Hendrik van Essen <hendrik.ve@fu-berlin.de>
 * @file
 */

#ifndef PCA9633_H
#define PCA9633_H

#ifdef __cplusplus
extern "C"
{
#endif

#include "stdbool.h"
#include "periph/i2c.h"

#include "pca9633_regs.h"

/**
 * @brief Blinking period with a maximum duration of ~10.73 s
 */
#define PCA9633_BLINKING_PERIOD_MAX_MS  (10625)

/**
 * @brief Ration between on/ off in blinking mode is balanced.
 *
 * 128 = 255 / 2
 */
#define PCA9633_BLINKING_RATIO_BALANCED 128

/**
 * @brief   PCA9633 device initialization parameters
 */
typedef struct {
    i2c_t i2c_dev;              /**< I2C device */
    uint16_t i2c_addr;          /**< I2C address of device */

    uint8_t reg_pwm_red;        /**< Register for red color */
    uint8_t reg_pwm_green;      /**< Register for green color */
    uint8_t reg_pwm_blue;       /**< Register for blue color */
    uint8_t reg_pwm_amber;      /**< Register for amber color */

    bool has_amber_channel;      /**< Whether PCA9633 has fourth channel */
} pca9633_params_t;

/**
 * @brief   PCA9633 PWM device data structure type
 */
typedef struct {
    pca9633_params_t params;     /**< Device initialization parameters */
    uint8_t stored_reg_ledout;   /**< Stored register content of LEDOUT */
} pca9633_t;

/**
 * @brief   PCA9633 driver error codes
 */
enum {
    PCA9633_OK              = 0,    /**< Success */
    PCA9633_ERROR_I2C       = 1,    /**< I2C communication error */
};

/**
 * @brief   PCA9633 PWM channel definitions
 */
typedef enum {
    PCA9633_PWM_CHANNEL_0 = PCA9633_REG_PWM0,   /**< PWM channel 0 */
    PCA9633_PWM_CHANNEL_1 = PCA9633_REG_PWM1,   /**< PWM channel 1 */
    PCA9633_PWM_CHANNEL_2 = PCA9633_REG_PWM2,   /**< PWM channel 2 */
    PCA9633_PWM_CHANNEL_3 = PCA9633_REG_PWM3,   /**< PWM channel 3 */
} pca9633_pwm_channel_t;

/**
 * @brief   LED driver output state, LEDOUT (page 14, below table 13)
 */
typedef enum {
    /**
     * @brief LED driver x is off
     */
    PCA9633_LDR_STATE_OFF,

    /**
     * @brief LED driver x is fully on (individual brightness and group
     *        dimming/ blinking not controlled)
     */
    PCA9633_LDR_STATE_ON,

    /**
     * @brief LED driver x individual brightness can be controlled through its
     *        PWMx register
     */
    PCA9633_LDR_STATE_IND,

    /**
     * @brief LED driver x individual brightness and group dimming/ blinking can
     *        be controlled through its PWMx register and the GRPPWM registers.
     *        If using PCA9633_LDR_STATE_IND_GRP the controller takes the
     *        minimum value of PWM* and GRPPWM register
     */
    PCA9633_LDR_STATE_IND_GRP,
} pca9633_ldr_state_t;

/**
 * @brief   Auto-Increment options (page 10, table 6)
 */
typedef enum {
    /**
     * @brief No Auto-Increment
     */
    PCA9633_AI_DISABLED,

    /**
     * @brief Auto-Increment for all registers. D3, D2, D1, D0 roll over to
     *        ‘0000’ after the last register (1100) is accessed.
     */
    PCA9633_AI_ALL,

    /**
     * @brief Auto-Increment for individual brightness registers only.
     *        D3, D2, D1, D0 roll over to ‘0010’ after the last register (0101)
     *        is accessed.
     */
    PCA9633_AI_IND,

    /**
     * @brief Auto-Increment for global control registers only. D3, D2, D1, D0
     *        roll over to ‘0110’ after the last register (0111) is accessed.
     */
    PCA9633_AI_GBL,

    /**
     * @brief Auto-Increment for individual and global control registers only.
     *        D3, D2, D1, D0 roll over to ‘0010’ after the last register (0111)
     *        is accessed.
     */
    PCA9633_AI_IND_GBL,
} pca9633_auto_inc_option_t;

/**
 * @brief   PCA9633 group control modes
 */
typedef enum {
    /**
     * @brief Control mode for blinking
     */
    PCA9633_GROUP_CONTROL_MODE_BLINKING,

    /**
     * @brief Control mode for dimming
     */
    PCA9633_GROUP_CONTROL_MODE_DIMMING,
} pca9633_group_control_mode_t;

/**
 * @brief Initialization.
 *
 * @param[in] dev       Device descriptor of the PCA9633
 * @param[in] params    Parameters for device initialization
 *
 * @return  PCA9633_OK on success
 * @return  -PCA9633_ERROR_I2C if acquiring of I2C bus fails
 * @return  -EIO When slave device doesn't ACK the byte
 * @return  -ENXIO When no devices respond on the address sent on the bus
 * @return  -ETIMEDOUT When timeout occurs before device's response
 * @return  -EINVAL When an invalid argument is given
 * @return  -EOPNOTSUPP When MCU driver doesn't support the flag operation
 * @return  -EAGAIN When a lost bus arbitration occurs
 */
int pca9633_init(pca9633_t *dev, const pca9633_params_t *params);

/**
 * @brief Turn on all LEDs. Restores settings saved at pca9633_turn_off().
 *
 * WARNING: If you call pca9633_turn_off() twice, without calling
 *          pca9633_turn_on() in between, then the restored state will be
 *          PCA9633_LDR_STATE_OFF!
 *
 * @param[in] dev       Device descriptor of the PCA9633
 */
void pca9633_turn_on(pca9633_t* dev);

/**
 * @brief Turn off all LEDs. Saves current settings for pca9633_turn_on().
 *        For power saving, see pca9633_sleep().
 *
 * WARNING: If you call pca9633_turn_off() twice, without calling
 *          pca9633_turn_on() in between, then the restored state will be
 *          PCA9633_LDR_STATE_OFF!
 *
 * @param[in] dev       Device descriptor of the PCA9633
 */
void pca9633_turn_off(pca9633_t* dev);

/**
 * @brief Switch to normal mode.
 *
 * @param[in] dev       Device descriptor of the PCA9633
 */
void pca9633_wakeup(pca9633_t* dev);

/**
 * @brief Switch to low power mode.
 *
 * @param[in] dev       Device descriptor of the PCA9633
 */
void pca9633_sleep(pca9633_t* dev);

/**
 * @brief Set individual PWM signal for a given channel.
 *
 * @param[in] dev           Device descriptor of the PCA9633
 * @param[in] pwm_channel   PWM channel
 * @param[in] pwm           PWM value
 */
void pca9633_set_pwm(pca9633_t* dev,
                     pca9633_pwm_channel_t pwm_channel, uint8_t pwm);

/**
 * @brief Set global PWM signal.
 *
 * @param[in] dev       Device descriptor of the PCA9633
 * @param[in] pwm       PWM value
 */
void pca9633_set_grp_pwm(pca9633_t* dev, uint8_t pwm);

/**
 * @brief Set up values for blinking mode. Blinking mode needs to be activated
 *        manually by calling
 *        pca9633_set_group_control_mode(GROUP_CONTROL_MODE_BLINKING).
 *
 * @param[in] dev               Device descriptor of the PCA9633
 * @param[in] blink_period_ms   Period in ms for one blink (turning off and on).
 *                              Maximum period possible is
 *                              PCA9633_BLINKING_PERIOD_MAX_MS ≈ 10.73 s. All
 *                              values above this maximum will we capped to it.
 * @param[in] on_off_ratio      Value between 0 and 255, where e.g. a value of
 *                              64 (255/4) means 1/4 of the time the LEDs are on
 *                              and 3/4 of the time the LEDs are off.
 */
void pca9633_set_blinking(pca9633_t* dev, uint16_t blink_period_ms,
                          uint8_t on_off_ratio);

/**
 * @brief Set PWM values for RGB.
 *
 * @param[in] dev       Device descriptor of the PCA9633
 * @param[in] r         Value for red color channel
 * @param[in] g         Value for green color channel
 * @param[in] b         Value for blue color channel
 */
void pca9633_set_rgb(pca9633_t* dev, uint8_t r, uint8_t g, uint8_t b);

/**
 * @brief Set PWM values for RGBA.
 *
 * @param[in] dev       Device descriptor of the PCA9633
 * @param[in] r         Value for red color channel
 * @param[in] g         Value for green color channel
 * @param[in] b         Value for blue color channel
 * @param[in] w         Value for amber color channel
 */
void pca9633_set_rgba(pca9633_t* dev, uint8_t r, uint8_t g, uint8_t b, uint8_t w);

/**
 * @brief Set the LED driver output state for a given channel.
 *        There are four states:
 *          - PCA9633_LDR_STATE_OFF
 *          - PCA9633_LDR_STATE_ON
 *          - PCA9633_LDR_STATE_IND
 *          - PCA9633_LDR_STATE_IND_GRP
 *
 * @param[in] dev           Device descriptor of the PCA9633
 * @param[in] state         One of the four possible states
 * @param[in] pwm_channel   PWM channel belonging to LDR
 */
void pca9633_set_ldr_state(pca9633_t* dev,
                           pca9633_ldr_state_t state,
                           pca9633_pwm_channel_t pwm_channel);

/**
 * @brief Set the LED driver output state for all channels.
 *        There are four states:
 *          - PCA9633_LDR_STATE_OFF
 *          - PCA9633_LDR_STATE_ON
 *          - PCA9633_LDR_STATE_IND
 *          - PCA9633_LDR_STATE_IND_GRP
 *
 * @param[in] dev       Device descriptor of the PCA9633
 * @param[in] state     One of the four possible states
 */
void pca9633_set_ldr_state_all(pca9633_t* dev, pca9633_ldr_state_t state);

/**
 * @brief Set an option for auto increment.
 *        There are five options:
 *          - PCA9633_AI_DISABLED
 *          - PCA9633_AI_ALL
 *          - PCA9633_AI_IND
 *          - PCA9633_AI_GBL
 *          - PCA9633_AI_IND_GBL
 *
 * @param[in] dev       Device descriptor of the PCA9633
 * @param[in] option    One of the possible five options
 */
void pca9633_set_auto_increment(pca9633_t* dev, pca9633_auto_inc_option_t option);

/**
 * @brief Set the group control mode.
 *        There are two modes:
 *          - PCA9633_GROUP_CONTROL_MODE_BLINKING
 *          - PCA9633_GROUP_CONTROL_MODE_DIMMING
 *
 * @param[in] dev       Device descriptor of the PCA9633
 * @param[in] mode      One of the two possible modes
 */
void pca9633_set_group_control_mode(pca9633_t* dev,
                                    pca9633_group_control_mode_t mode);

#ifdef __cplusplus
}
#endif

#endif /* PCA9633_H */
/** @} */