/******************************************************************************
*
* Revision: A
* Author: MAW
* Created:  10 Jan 2018
*
* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*  FILE: LP5521.h
* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*
* DESCRIPTION: Header file to define functions for the I2C 3 Channel LED driver.
*
*
* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*	Copyright (c) 2018, Metrohm Raman
* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/

#ifndef _LP5521_H_
#define _LP5521_H_

/* I2C Address */
#define LP5521_ADDRESS          0x32

/* Registers */
#define LP5521_REG_ENABLE       0x00
#define LP5521_REG_OP_MODE      0x01
#define LP5521_REG_R_PWM        0x02
#define LP5521_REG_G_PWM        0x03
#define LP5521_REG_B_PWM        0x04
#define LP5521_REG_R_CURRENT    0x05
#define LP5521_REG_G_CURRENT    0x06
#define LP5521_REG_B_CURRENT    0x07
#define LP5521_REG_CONFIG       0x08
#define LP5521_REG_STATUS       0x0C
#define LP5521_REG_RESET        0x0D
#define LP5521_REG_R_PROG_MEM   0x10
#define LP5521_REG_G_PROG_MEM   0x30
#define LP5521_REG_B_PROG_MEM   0x50

/* Base register to set LED current */
#define LP5521_REG_LED_CURRENT_BASE LP5521_REG_R_CURRENT
/* Base register to set the brightness */
#define LP5521_REG_LED_PWM_BASE     LP5521_REG_R_PWM

/* Bits in ENABLE register */
#define LP5521_MASTER_ENABLE        0x40    /* Chip master enable */
#define LP5521_LOGARITHMIC_PWM      0x80    /* Logarithmic PWM adjustment */
#define LP5521_EXEC_RUN             0x2A
#define LP5521_ENABLE_DEFAULT       (LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM)
#define LP5521_ENABLE_RUN_PROGRAM   (LP5521_ENABLE_DEFAULT | LP5521_EXEC_RUN)

/* General Constants */
#define LP5521_PROGRAM_LENGTH   32
#define LP5521_MAX_LEDS         3
#define LP5521_CMD_DIRECT       0x3F

/* CONFIG register */
#define LP5521_PWM_HF           0x40    /* PWM: 0 = 256Hz, 1 = 558Hz */
#define LP5521_PWRSAVE_EN       0x20    /* 1 = Power save mode */
#define LP5521_CP_MODE_OFF      0       /* Charge pump (CP) off */
#define LP5521_CP_MODE_BYPASS   8       /* CP forced to bypass mode */
#define LP5521_CP_MODE_1X5      0x10    /* CP forced to 1.5x mode */
#define LP5521_CP_MODE_AUTO     0x18    /* Automatic mode selection */
#define LP5521_R_TO_BATT        0x04    /* R out: 0 = CP, 1 = Vbat */
#define LP5521_CLK_INT          0x01    /* Internal clock */
#define LP5521_DEFAULT_CFG      (LP5521_PWM_HF | LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO)

/* Status */
#define LP5521_EXT_CLK_USED     0x08

/* default R channel current register value */
#define LP5521_REG_R_CURR_DEFAULT   0xAF

/* Reset register value */
#define LP5521_RESET            0xFF

/* Program Memory Operations */
#define LP5521_MODE_R_M         0x30    /* Operation Mode Register */
#define LP5521_MODE_G_M         0x0C
#define LP5521_MODE_B_M         0x03
#define LP5521_LOAD_R           0x10
#define LP5521_LOAD_G           0x04
#define LP5521_LOAD_B           0x01

#define LP5521_R_IS_LOADING(mode)  ((mode & LP5521_MODE_R_M) == LP5521_LOAD_R)
#define LP5521_G_IS_LOADING(mode)  ((mode & LP5521_MODE_G_M) == LP5521_LOAD_G)
#define LP5521_B_IS_LOADING(mode)  ((mode & LP5521_MODE_B_M) == LP5521_LOAD_B)

#define LP5521_EXEC_R_M         0x30    /* Enable Register */
#define LP5521_EXEC_G_M         0x0C
#define LP5521_EXEC_B_M         0x03
#define LP5521_EXEC_M           0x3F
#define LP5521_RUN_R            0x20
#define LP5521_RUN_G            0x08
#define LP5521_RUN_B            0x02

enum lp55xx_engine_index {
    LP55XX_ENGINE_INVALID,
    LP55XX_ENGINE_1,
    LP55XX_ENGINE_2,
    LP55XX_ENGINE_3,
    LP55XX_ENGINE_MAX = LP55XX_ENGINE_3,
};

enum lp55xx_engine_mode {
    LP55XX_ENGINE_DISABLED,
    LP55XX_ENGINE_LOAD,
    LP55XX_ENGINE_RUN,
};


/*
 * struct lp55xx_reg
 * @addr : Register address
 * @val  : Register value
 */
struct lp55xx_reg
{
    uint8_t addr;
    uint8_t val;
};

/*
 * struct lp55xx_engine
 * @mode       : Engine mode
 * @led_mux    : Mux bits for LED selection. Only used in LP5523
 */
struct lp55xx_engine {
    enum lp55xx_engine_mode mode;
    uint16_t led_mux;
};

/*
 * struct lp55xx_chip
 * @cl         : I2C communication for access registers
 * @pdata      : Platform specific data
 * @lock       : Lock for user-space interface
 * @num_leds   : Number of registered LEDs
 * @cfg        : Device specific configuration data
 * @engine_idx : Selected engine number
 * @engines    : Engine structure for the device attribute R/W interface
 * @fw         : Firmware data for running a LED pattern
 */
struct lp55xx_chip {
    struct i2c_client *cl;
    struct clk *clk;
    struct lp55xx_platform_data *pdata;
    int num_leds;
    struct lp55xx_device_config *cfg;
    enum lp55xx_engine_index engine_idx;
    struct lp55xx_engine engines[LP55XX_ENGINE_MAX];
    const struct firmware *fw;
};

/*
 * struct lp55xx_led
 * @chan_nr         : Channel number
 * @cdev            : LED class device
 * @led_current     : Current setting at each led channel
 * @max_current     : Maximun current at each led channel
 * @brightness      : Brightness value
 */
struct lp55xx_led {
    int chan_nr;
    uint8_t led_current;
    uint8_t max_current;
    uint8_t brightness;
};

/* register access */
extern int lp55xx_write(uint8_t reg, uint8_t val);
extern int lp55xx_read(uint8_t reg, uint8_t *val);
extern int lp55xx_update_bits(uint8_t reg, uint8_t mask, uint8_t val);

/* external clock detection */
extern bool lp55xx_is_extclk_used(struct lp55xx_chip *chip);

/* common device init/deinit functions */
extern int lp55xx_init_device(struct lp55xx_chip *chip);
extern void lp55xx_deinit_device(struct lp55xx_chip *chip);

extern inline void lp5521_wait_enable_done(void);
extern void lp5521_set_led_current(struct lp55xx_led *led, uint8_t led_current);
extern void lp5521_load_engine(struct lp55xx_chip *chip);
extern void lp5521_stop_all_engines(struct lp55xx_chip *chip);
extern void lp5521_stop_engine(struct lp55xx_chip *chip);
extern void lp5521_run_engine(struct lp55xx_chip *chip, bool start);
extern int lp5521_update_program_memory(struct lp55xx_chip *chip,
                    const uint8_t *data, size_t size);
extern void lp5521_firmware_loaded(struct lp55xx_chip *chip);
extern int lp5521_init(void);
extern int lp5521_run_selftest(struct lp55xx_chip *chip, char *buf);

extern int lp5521_led_brightness(struct lp55xx_led *led);

extern ssize_t show_engine_mode(struct device *dev,
                struct device_attribute *attr, char *buf, int nr);

extern ssize_t store_engine_mode(struct device *dev,
                 struct device_attribute *attr,
                 const char *buf, size_t len, int nr);

extern ssize_t store_engine_load(struct device *dev,
                 struct device_attribute *attr,
                 const char *buf, size_t len, int nr);

extern ssize_t lp5521_selftest(struct device *dev,
                   struct device_attribute *attr,
                   char *buf);

#endif

/******************************************************************************
* Done.
*/
