diff options
Diffstat (limited to 'drivers/leds/rt5033_fled.c')
| -rw-r--r-- | drivers/leds/rt5033_fled.c | 947 |
1 files changed, 947 insertions, 0 deletions
diff --git a/drivers/leds/rt5033_fled.c b/drivers/leds/rt5033_fled.c new file mode 100644 index 00000000..81f75cdd --- /dev/null +++ b/drivers/leds/rt5033_fled.c @@ -0,0 +1,947 @@ +/* drivers/leds/rt5033_fled.c + * RT5033 Flash LED Driver + * + * Copyright (C) 2013 Richtek Technology Corp. + * Author: Patrick Chang <patrick_chang@richtek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/leds/rtfled.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/mfd/rt5033.h> +#include <linux/mfd/rt5033_irq.h> +#include <linux/leds/rt5033_fled.h> +#include <linux/delay.h> +#include <linux/power_supply.h> +#include <linux/version.h> +#include <linux/of.h> + +#define ALIAS_NAME "rt5033-fled" + +#define RT5033_FLED_INFO(format, args...) \ + printk(KERN_INFO "%s:%s() line-%d: " format, \ + ALIAS_NAME, __FUNCTION__, __LINE__, ## args) +#define RT5033_FLED_WARN(format, args...) \ + printk(KERN_WARNING "%s:%s() line-%d: " format, \ + ALIAS_NAME, __FUNCTION__, __LINE__, ## args) +#define RT5033_FLED_ERR(format, args...) \ + printk(KERN_ERR "%s:%s() line-%d: " format, \ + ALIAS_NAME, __FUNCTION__, __LINE__, ## args) + + +#define RT5033_FLED_PIN_CTRL (1<<4) + +#define EN_FLED_IRQ 0 + +typedef struct rt5033_fled_info { + rt_fled_info_t base; + const rt5033_fled_platform_data_t *pdata; + rt5033_mfd_chip_t *chip; + struct mutex led_lock; + struct i2c_client *i2c_client; + int torch_current; + int strobe_current; + int boost : 1; + int strobe_status : 1; + int ta_exist : 1; + int led_count : 2; +} rt5033_fled_info_t; + + +static struct platform_device rt_fled_pdev = { + .name = "rt-flash-led", + .id = -1, +}; + +#define RT5033_OFF_EVENT_NRD 0x6B +#define FORCE_NR 0x01 + +static int rt5033_fled_set_ta_status(struct i2c_client *iic, + int ta_good_and_exist); + +static int rt5033_set_fled_osc_en(struct i2c_client *iic, int en) +{ + return (en ? rt5033_set_bits : rt5033_clr_bits)(iic, 0x1a, (1 << 5)); +} +#ifdef CONFIG_CHARGER_RT5033 +extern int rt5033_chg_fled_init(struct i2c_client *client); +#endif + +static int rt5033_fled_init(struct rt_fled_info *fled_info) +{ + rt5033_fled_info_t *info = (rt5033_fled_info_t *)fled_info; + rt5033_mfd_platform_data_t *mfd_pdata; + BUG_ON(info == NULL); + mfd_pdata = info->chip->pdata; + mutex_lock(&info->led_lock); + rt5033_set_bits(info->i2c_client, RT5033_FLED_RESET, 0x80); + rt5033_fled_set_ta_status(info->i2c_client, 0); +#ifdef CONFIG_CHARGER_RT5033 + rt5033_chg_fled_init(info->i2c_client); +#else + /* Force to do normal read (read from e-fuse) ==> let FLED current be more accurate */ + rt5033_set_bits(info->i2c_client, RT5033_OFF_EVENT_NRD, FORCE_NR); + /* Delay 100 us to wait for normal read complete */ + udelay(100); + /* Finsh normal read and clear FORCE_NR bit */ + rt5033_clr_bits(info->i2c_client, RT5033_OFF_EVENT_NRD, FORCE_NR); +#endif + if (!info->pdata->fled1_en) + rt5033_clr_bits(info->i2c_client, RT5033_FLED_FUNCTION1, 0x01); + if (!info->pdata->fled2_en) + rt5033_clr_bits(info->i2c_client, RT5033_FLED_FUNCTION1, 0x02); + if (info->pdata->fled_mid_track_alive) + rt5033_set_bits(info->i2c_client, RT5033_FLED_CONTROL2, (1 << 6)); + if (info->pdata->fled_mid_auto_track_en) + rt5033_set_bits(info->i2c_client, RT5033_FLED_CONTROL2, (1 << 7)); + rt5033_reg_write(info->i2c_client, RT5033_FLED_STROBE_CONTROL1, + (info->pdata->fled_timeout_current_level << 5) | + info->pdata->fled_strobe_current); + info->base.flashlight_dev->props.strobe_brightness = + info->pdata->fled_strobe_current; + + rt5033_reg_write(info->i2c_client, RT5033_FLED_STROBE_CONTROL2, + info->pdata->fled_strobe_timeout); + info->base.flashlight_dev->props.strobe_timeout = + info->base.hal->fled_strobe_timeout_list(fled_info, + info->pdata->fled_strobe_timeout); + RT5033_FLED_INFO("Strobe timeout = %d ms\n", + info->base.flashlight_dev->props.strobe_timeout); + rt5033_reg_write(info->i2c_client, RT5033_FLED_CONTROL1, + (info->pdata->fled_torch_current << 4) | + info->pdata->fled_lv_protection); + info->base.flashlight_dev->props.torch_brightness = + info->pdata->fled_torch_current; + rt5033_assign_bits(info->i2c_client, RT5033_FLED_CONTROL2, + 0x3f, info->pdata->fled_mid_level); + info->led_count = info->pdata->fled1_en + info->pdata->fled2_en; +#ifdef CONFIG_FLED_RT5033_I2C + rt5033_set_bits(info->i2c_client, RT5033_FLED_FUNCTION1, RT5033_FLED_PIN_CTRL); +#endif + mutex_unlock(&info->led_lock); + return 0; +} + +static int rt5033_fled_suspend(struct rt_fled_info *info, pm_message_t state) +{ + RT5033_FLED_INFO("Suspend\n"); + return 0; +} + +static int rt5033_fled_resume(struct rt_fled_info *info) +{ + RT5033_FLED_INFO("Resume\n"); + return 0; +} + +static int rt5033_fled_set_ta_status(struct i2c_client *iic, int ta_good_and_exist) +{ + return rt5033_assign_bits(iic, RT5033_FLED_CONTROL5, (0x03 << 6), + ta_good_and_exist ? (0x03 << 6) : 0); + +} + +inline static int rt5033_set_uug_status(struct i2c_client *iic, int uug) +{ + return rt5033_assign_bits(iic, 0x19, 0x02, uug); +} + +#ifdef CONFIG_FLED_RT5033_EXT_GPIO +/* For GPIO operation, camera driver must use lock / unlock funtion */
void rt5033_fled_strobe_critial_section_lock(struct rt_fled_info *fled_info) +{ + rt5033_fled_info_t *info = (rt5033_fled_info_t *)fled_info; + BUG_ON(info == NULL); + rt5033_fled_lock(fled_info); + rt5033_fled_set_ta_status(info->i2c_client, 0); +} +EXPORT_SYMBOL(rt5033_fled_strobe_critial_section_lock); + +void rt5033_fled_strobe_critial_section_unlock(struct rt_fled_info *fled_info) +{ + rt5033_fled_info_t *info = (rt5033_fled_info_t *)fled_info; + BUG_ON(info == NULL); + rt5033_fled_set_ta_status(info->i2c_client, info->ta_exist); + rt5033_fled_unlock(fled_info); +} +EXPORT_SYMBOL(rt5033_fled_strobe_critial_section_unlock); +#endif /* CONFIG_FLED_RT5033_EXT_GPIO */ + +#ifdef CONFIG_FLED_RT5033_EXT_GPIO +int32_t rt5033_fled_enable(struct rt_fled_info *fled_info, + int enable) +{ + int32_t ret; + rt5033_fled_info_t *info = + (rt5033_fled_info_t *)fled_info; + if (enable) + ret = rt5033_clr_bits(info->i2c_client, + RT5033_FLED_FUNCTION2, 0x01); + else { + rt5033_set_fled_osc_en(info->i2c_client, 1); + ret = rt5033_set_bits(info->i2c_client, + RT5033_FLED_FUNCTION2, 0x01); + rt5033_set_fled_osc_en(info->i2c_client, 0); + } + return ret; +} +EXPORT_SYMBOL(rt5033_fled_enable); +#endif + + +#ifdef CONFIG_FLED_RT5033_EXT_GPIO +/* For GPIO operation */ +int32_t rt5033_charger_notification(struct rt_fled_info *fled_info, + int32_t attach) +{ + rt5033_fled_info_t *info = (rt5033_fled_info_t *)fled_info; + int force_torch_en = 0; + int chg_status; + int reg0x1a; + BUG_ON(info == NULL); + rt5033_fled_lock(fled_info); + info->ta_exist = attach; + reg0x1a = rt5033_reg_read(info->i2c_client, 0x1a); + reg0x1a |= 0xa0; + rt5033_fled_set_ta_status(info->i2c_client, attach); + if (attach == 0 && info->boost == 0) { + chg_status = rt5033_reg_read(info->i2c_client, 0x00); + /* remove TA, re-start FlashEN, + * * and then become boost mode => torch enabled */ + force_torch_en = (chg_status & 0x08) ? 1 : 0; + /* Enable hidden bit (Force boosting) for TA/USB detaching + * To fix flicking issue for torch while TA is removing + */ + if (force_torch_en) + rt5033_reg_write(info->i2c_client, 0x1a, reg0x1a); + } + rt5033_set_uug_status(info->i2c_client, attach ? 0x02 : 0x00); + + if (attach) { + /* GPIO mode, 0x1 means disable + * * Disable it and then enable it */ + rt5033_assign_bits(info->i2c_client, RT5033_FLED_FUNCTION2, 0x81, 0x1); + rt5033_assign_bits(info->i2c_client, RT5033_FLED_FUNCTION2, 0x81, 0x0); + } + + rt5033_fled_unlock(fled_info); + /* Disable hidden bit (Force boosting) for TA/USB detaching + * To fix flicking issue for torch while TA is removing + */ + if (force_torch_en) { + udelay(2500); + rt5033_clr_bits(info->i2c_client, 0x1a, 0x80); + } + RT5033_FLED_INFO("force_torch_en = %d\n", + force_torch_en); + return 0; +} +#else +int32_t rt5033_charger_notification(struct rt_fled_info *fled_info, + int32_t attach) +{ + rt5033_fled_info_t *info = (rt5033_fled_info_t *)fled_info; + int mode = fled_info->hal->fled_get_mode(fled_info); + BUG_ON(info == NULL); + rt5033_fled_lock(fled_info); + info->ta_exist = attach; + /* Enable hidden bit (Force boosting) for TA/USB detaching + * To fix flicking issue for torch while TA is removing + */ + if (attach == 0) { + /* For i2c FlashLED operation, + * we will check torch had already been on or not + */ + if (mode == FLASHLIGHT_MODE_TORCH || mode == FLASHLIGHT_MODE_MIXED) + rt5033_set_bits(info->i2c_client, 0x1a, 0x80); + } + rt5033_fled_set_ta_status(info->i2c_client, attach); + rt5033_set_uug_status(info->i2c_client, attach ? 0x02 : 0x00); + + if (mode == FLASHLIGHT_MODE_TORCH || mode == FLASHLIGHT_MODE_MIXED) { + /* disable FlashEN and then enable it*/ + rt5033_assign_bits(info->i2c_client, RT5033_FLED_FUNCTION2, 0x81, 0x0); + rt5033_assign_bits(info->i2c_client, RT5033_FLED_FUNCTION2, 0x81, 0x1); + } + rt5033_fled_unlock(fled_info); + /* Disable hidden bit (Force boosting) for TA/USB detaching + * To fix flicking issue for torch while TA is removing + */ + if (attach == 0) { + /* For i2c FlashLED operation, + * we will check torch had already been on or not + */ + if (mode == FLASHLIGHT_MODE_TORCH || mode == FLASHLIGHT_MODE_MIXED) { + usleep_range(2500, 2600); + rt5033_clr_bits(info->i2c_client, 0x1a, 0x80); + } + } + return 0; +} +#endif +EXPORT_SYMBOL(rt5033_charger_notification); + +int32_t rt5033_boost_notification(struct rt_fled_info *fled_info, int32_t on) +{ + rt5033_fled_info_t *info = (rt5033_fled_info_t *)fled_info; + BUG_ON(info == NULL); + rt5033_fled_lock(fled_info); + info->boost = on; +#ifdef CONFIG_FLED_RT5033_EXT_GPIO + info->ta_exist = on; + rt5033_fled_set_ta_status(info->i2c_client, on); +#endif + rt5033_set_uug_status(info->i2c_client, on ? 0x02 : 0x00); + rt5033_fled_unlock(fled_info); + return 0; +} +EXPORT_SYMBOL(rt5033_boost_notification); + +static int rt5033_fled_set_mode(struct rt_fled_info *fled_info, + flashlight_mode_t mode) +{ + rt5033_fled_info_t *info = (rt5033_fled_info_t *)fled_info; + + if (info->strobe_status) { + info->strobe_status = 0; + rt5033_fled_set_ta_status(info->i2c_client, info->ta_exist); + rt5033_set_uug_status(info->i2c_client, + (info->ta_exist | info->boost) ? 0x02 : 0x00); + rt5033_fled_unlock(fled_info); + } + rt5033_fled_lock(fled_info); + switch (mode) { + case FLASHLIGHT_MODE_OFF: + rt5033_clr_bits(info->i2c_client, RT5033_FLED_FUNCTION2, 0x80); + usleep_range(500, 1000); + rt5033_clr_bits(info->i2c_client, RT5033_FLED_FUNCTION2, 0x01); + rt5033_set_fled_osc_en(info->i2c_client, 0); + + break; + case FLASHLIGHT_MODE_TORCH: + case FLASHLIGHT_MODE_MIXED: + rt5033_clr_bits(info->i2c_client, RT5033_FLED_FUNCTION1, 0x04); + rt5033_assign_bits(info->i2c_client, RT5033_FLED_FUNCTION2, 0x81, 0x1); + break; + case FLASHLIGHT_MODE_FLASH: + rt5033_assign_bits(info->i2c_client, RT5033_FLED_FUNCTION2, 0x81, 0x0); + rt5033_set_bits(info->i2c_client, RT5033_FLED_FUNCTION1, 0x04); + rt5033_assign_bits(info->i2c_client, RT5033_FLED_FUNCTION2, 0x81, 0x1); + break; + default: + return -EINVAL; + } + rt5033_fled_unlock(fled_info); + info->base.flashlight_dev->props.mode = mode; + return 0; +} + +static int rt5033_fled_get_mode(struct rt_fled_info *info) +{ + rt5033_fled_info_t *rt5033_fled_info = (rt5033_fled_info_t *)info; + return rt5033_fled_info->base.flashlight_dev->props.mode; +} + +void rt5033_fled_lock(struct rt_fled_info *fled_info) +{ + rt5033_fled_info_t *info = (rt5033_fled_info_t *)fled_info; + mutex_lock(&info->led_lock); +} +EXPORT_SYMBOL(rt5033_fled_lock); + +void rt5033_fled_unlock(struct rt_fled_info *fled_info) +{ + rt5033_fled_info_t *info = (rt5033_fled_info_t *)fled_info; + mutex_unlock(&info->led_lock); +} +EXPORT_SYMBOL(rt5033_fled_unlock); + +static int rt5033_fled_strobe(struct rt_fled_info *fled_info) +{ + int ret = 0; + rt5033_fled_info_t *info = (rt5033_fled_info_t *)fled_info; + rt5033_set_fled_osc_en(info->i2c_client, 1); + if (info->strobe_status == 0) { + /* Lock LED until setting to OFF MODE*/ + rt5033_fled_lock(fled_info); + info->strobe_status = 1; + rt5033_fled_set_ta_status(info->i2c_client, 0); + rt5033_set_uug_status(info->i2c_client, 0); + } + switch (info->base.flashlight_dev->props.mode) { + case FLASHLIGHT_MODE_FLASH: + rt5033_set_bits(info->i2c_client, RT5033_FLED_FUNCTION2, 0x81); + break; + case FLASHLIGHT_MODE_MIXED: + rt5033_assign_bits(info->i2c_client, RT5033_FLED_FUNCTION2, 0x81, 0x1); + rt5033_set_bits(info->i2c_client, RT5033_FLED_FUNCTION1, 0x04); + rt5033_clr_bits(info->i2c_client, RT5033_FLED_CONTROL2, + (1 << 7)); // DISABLE AUTO TRACK + rt5033_set_bits(info->i2c_client, RT5033_FLED_FUNCTION2, 0x81); + break; + default: + RT5033_FLED_ERR("Error : not flash / mixed mode\n"); + ret = -EINVAL; + } + return ret; +} + +static int torch_current[] = { + 12500, + 25000, + 37500, + 50000, + 62500, + 75000, + 87500, + 100000, + 112500, + 125000, + 137500, + 150000, + 162500, + 175000, + 187500, + 200000, +}; + + +/* Return value : -EINVAL => selector parameter is out of range, otherwise current in mA*/ +static int rt5033_fled_troch_current_list(struct rt_fled_info *info, + int selector) +{ + if (selector < 0 || selector >= ARRAY_SIZE(torch_current)) + return -EINVAL; + return torch_current[selector]; +} + + +static int rt5033_fled_strobe_current_list(struct rt_fled_info *info, + int selector) +{ + if (selector < 0 || selector >= 31) + return -EINVAL; + return (50 + selector * 25) * 1000; +} + +static int strobe_timeout_level[] = { + 50000, + 75000, + 100000, + 125000, + 150000, + 175000, + 200000, +}; + + +static int rt5033_fled_timeout_level_list(struct rt_fled_info *info, + int selector) +{ + if (selector < 0 || selector >= ARRAY_SIZE(strobe_timeout_level)) + return -EINVAL; + return strobe_timeout_level[selector]; +} +static int lv_protection[] = { + 2900, + 3000, + 3100, + 3200, + 3300, + 3400, + 3500, + 3600, +}; + + +static struct flashlight_properties rt5033_fled_props = { + .type = FLASHLIGHT_TYPE_LED, + .torch_brightness = 0, + .torch_max_brightness = ARRAY_SIZE(torch_current) - 1, + .strobe_brightness = 0, + .strobe_max_brightness = 31 - 1, + .strobe_delay = 2, + .strobe_timeout = 64, + .alias_name = "rt5033-fled", +}; + +/* Return value : -EINVAL => selector parameter is out of range, otherwise voltage in mV*/ +static int rt5033_fled_lv_protection_list(struct rt_fled_info *info, + int selector) +{ + if (selector < 0 || selector >= ARRAY_SIZE(lv_protection)) + return -EINVAL; + return lv_protection[selector]; +} +/* Return value : -EINVAL => selector parameter is out of range, otherwise time in ms*/ +static int rt5033_fled_strobe_timeout_list(struct rt_fled_info *info, + int selector) +{ + if (selector < 0 || selector >= 37) + return -EINVAL; + return (64 + selector * 32); +} + +static int rt5033_fled_set_torch_current_sel(struct rt_fled_info *fled_info, + int selector) +{ + int rc; + rt5033_fled_info_t *info = (rt5033_fled_info_t *)fled_info; + RT5033_FLED_INFO("Set torch current to %d\n", selector); + if (selector < 0 || selector > info-> + base.flashlight_dev->props.torch_max_brightness) + return -EINVAL; + rc = rt5033_assign_bits(info->i2c_client, RT5033_FLED_CONTROL1, + 0xf0, selector << 4); + if (rc == 0) + info->base.flashlight_dev->props.torch_brightness = selector; + return rc; +} +static int rt5033_fled_set_strobe_current_sel(struct rt_fled_info *fled_info, + int selector) +{ + int rc; + rt5033_fled_info_t *info = (rt5033_fled_info_t *)fled_info; + RT5033_FLED_INFO("Set strobe current to %d\n", selector); + if (selector < 0 || selector > info-> + base.flashlight_dev->props.strobe_max_brightness) + return -EINVAL; + rc = rt5033_assign_bits(info->i2c_client, RT5033_FLED_STROBE_CONTROL1, + 0x1f, selector); + if (rc == 0) + info->base.flashlight_dev->props.strobe_brightness = selector; + return 0; +} +static int rt5033_fled_set_timeout_level_sel(struct rt_fled_info *fled_info, + int selector) +{ + rt5033_fled_info_t *info = (rt5033_fled_info_t *)fled_info; + RT5033_FLED_INFO("Set timeout level to %d\n", selector); + if (selector < 0 || selector >= ARRAY_SIZE(strobe_timeout_level)) + return -EINVAL; + return rt5033_assign_bits(info->i2c_client, RT5033_FLED_STROBE_CONTROL1, + 0xe0, selector << 5); + +} + + +static int rt5033_fled_set_lv_protection_sel(struct rt_fled_info *fled_info, + int selector) +{ + rt5033_fled_info_t *info = (rt5033_fled_info_t *)fled_info; + RT5033_FLED_INFO("Set lv protection to %d\n", selector); + if (selector < 0 || selector >= ARRAY_SIZE(lv_protection)) + return -EINVAL; + + return rt5033_assign_bits(info->i2c_client, RT5033_FLED_CONTROL1, + 0x07, selector); +} +static int rt5033_fled_set_strobe_timeout_sel(struct rt_fled_info *fled_info, + int selector) +{ + int rc; + rt5033_fled_info_t *info = (rt5033_fled_info_t *)fled_info; + RT5033_FLED_INFO("Set strobe timeout to %d\n", selector); + if (selector < 0 || selector >= 37) + return -EINVAL; + rc = rt5033_assign_bits(info->i2c_client, RT5033_FLED_STROBE_CONTROL2, + 0x3f, selector); + if (rc == 0) + fled_info->flashlight_dev->props.strobe_timeout = + rt5033_fled_strobe_timeout_list(fled_info, selector); + return rc; +} + +static int rt5033_fled_get_torch_current_sel(struct rt_fled_info *fled_info) +{ + int rc; + rt5033_fled_info_t *info = (rt5033_fled_info_t *)fled_info; + rc = rt5033_reg_read(info->i2c_client, RT5033_FLED_CONTROL1); + if (rc < 0) + return rc; + return (rc & 0xf0) >> 4; +} + +static int rt5033_fled_get_strobe_current_sel(struct rt_fled_info *fled_info) +{ + int rc; + rt5033_fled_info_t *info = (rt5033_fled_info_t *)fled_info; + rc = rt5033_reg_read(info->i2c_client, RT5033_FLED_STROBE_CONTROL1); + if (rc < 0) + return rc; + return rc & 0x1f; +} + +static int rt5033_fled_get_timeout_level_sel(struct rt_fled_info *fled_info) +{ + int rc; + rt5033_fled_info_t *info = (rt5033_fled_info_t *)fled_info; + rc = rt5033_reg_read(info->i2c_client, RT5033_FLED_STROBE_CONTROL1); + if (rc < 0) + return rc; + return (rc & 0xe0) >> 5; +} + +static int rt5033_fled_get_lv_protection_sel(struct rt_fled_info *fled_info) +{ + int rc; + rt5033_fled_info_t *info = (rt5033_fled_info_t *)fled_info; + rc = rt5033_reg_read(info->i2c_client, RT5033_FLED_CONTROL1); + if (rc < 0) + return rc; + return rc & 0x07; +} + +static int rt5033_fled_get_strobe_timeout_sel(struct rt_fled_info *fled_info) +{ + int rc; + rt5033_fled_info_t *info = (rt5033_fled_info_t *)fled_info; + rc = rt5033_reg_read(info->i2c_client, RT5033_FLED_STROBE_CONTROL2); + if (rc < 0) + return rc; + return rc & 0x3f; +} + +static void rt5033_fled_shutdown(struct rt_fled_info *info) +{ + flashlight_set_mode(info->flashlight_dev, FLASHLIGHT_MODE_OFF); + return; +} + +static struct rt_fled_hal rt5033_fled_hal = { + .fled_init = rt5033_fled_init, + .fled_suspend = rt5033_fled_suspend, + .fled_resume = rt5033_fled_resume, + .fled_set_mode = rt5033_fled_set_mode, + .fled_get_mode = rt5033_fled_get_mode, + .fled_strobe = rt5033_fled_strobe, + .fled_troch_current_list = rt5033_fled_troch_current_list, + .fled_strobe_current_list = rt5033_fled_strobe_current_list, + .fled_timeout_level_list = rt5033_fled_timeout_level_list, + .fled_lv_protection_list = rt5033_fled_lv_protection_list, + .fled_strobe_timeout_list = rt5033_fled_strobe_timeout_list, + /* method to set */ + .fled_set_torch_current_sel = rt5033_fled_set_torch_current_sel, + .fled_set_strobe_current_sel = rt5033_fled_set_strobe_current_sel, + .fled_set_timeout_level_sel = rt5033_fled_set_timeout_level_sel, + + .fled_set_lv_protection_sel = rt5033_fled_set_lv_protection_sel, + .fled_set_strobe_timeout_sel = rt5033_fled_set_strobe_timeout_sel, + + /* method to get */ + .fled_get_torch_current_sel = rt5033_fled_get_torch_current_sel, + .fled_get_strobe_current_sel = rt5033_fled_get_strobe_current_sel, + .fled_get_timeout_level_sel = rt5033_fled_get_timeout_level_sel, + .fled_get_lv_protection_sel = rt5033_fled_get_lv_protection_sel, + .fled_get_strobe_timeout_sel = rt5033_fled_get_strobe_timeout_sel, + /* PM shutdown, optional */ + .fled_shutdown = rt5033_fled_shutdown, + +}; + + +static rt5033_fled_platform_data_t rt5033_default_fled_pdata = { + .fled1_en = 1, + .fled2_en = 1, + .fled_mid_track_alive = 0, + .fled_mid_auto_track_en = 0, + .fled_timeout_current_level = RT5033_TIMEOUT_LEVEL(50), + .fled_strobe_current = RT5033_STROBE_CURRENT(750), + .fled_strobe_timeout = RT5033_STROBE_TIMEOUT(544), + .fled_torch_current = RT5033_TORCH_CURRENT(38), + .fled_lv_protection = RT5033_LV_PROTECTION(3200), + .fled_mid_level = RT5033_MID_REGULATION_LEVEL(5000), +}; + + +#define FLAG_HIGH (0x01) +#define FLAG_LOW (0x02) +#define FLAG_LOW_TO_HIGH (0x04) +#define FLAG_HIGH_TO_LOW (0x08) +#define FLAG_CHANGED (FLAG_LOW_TO_HIGH|FLAG_HIGH_TO_LOW) + +struct rt5033_fled_irq_handler { + const char *name; + int irq_index; + irqreturn_t (*handler)(int irq, void *data); +}; +#if EN_FLED_IRQ +static irqreturn_t rt5033_vf_l_irq_handler(int irq, void *data) +{ + rt5033_fled_info_t *info = data; + RT5033_FLED_WARN("LED VF Low\n"); + BUG_ON(info == NULL); + return IRQ_HANDLED; +} + +static irqreturn_t rt5033_ledcs2_short_irq_handler(int irq, void *data) +{ + rt5033_fled_info_t *info = data; + RT5033_FLED_WARN("LEDCS2 short\n"); + BUG_ON(info == NULL); + return IRQ_HANDLED; +} + +static irqreturn_t rt5033_ledcs1_short_irq_handler(int irq, void *data) +{ + rt5033_fled_info_t *info = data; + RT5033_FLED_WARN("LEDCS1 short\n"); + BUG_ON(info == NULL); + return IRQ_HANDLED; +} +#endif //#EN_FLED_IRQ + +const struct rt5033_fled_irq_handler rt5033_fled_irq_handlers[] = { +#if EN_FLED_IRQ + { + .name = "VF_L", + .handler = rt5033_vf_l_irq_handler, + .irq_index = RT5033_VF_L_IRQ, + + }, + { + .name = "LEDCS2_SHORT", + .handler = rt5033_ledcs2_short_irq_handler, + .irq_index = RT5033_LEDCS2_SHORT_IRQ, + }, + { + .name = "LEDCS1_SHORT", + .handler = rt5033_ledcs1_short_irq_handler, + .irq_index = RT5033_LEDCS1_SHORT_IRQ, + }, +#endif //EN_FLED_IRQ +}; + +static int register_irq(struct platform_device *pdev, + rt5033_fled_info_t *info) +{ + int irq; + int i, j; + int ret; + const struct rt5033_fled_irq_handler *irq_handler = rt5033_fled_irq_handlers; + const char *irq_name; + for (i = 0; i < ARRAY_SIZE(rt5033_fled_irq_handlers); i++) { + irq_name = rt5033_get_irq_name_by_index(irq_handler[i].irq_index); + irq = platform_get_irq_byname(pdev, irq_name); + ret = request_threaded_irq(irq, NULL, irq_handler[i].handler, + IRQF_ONESHOT | IRQF_TRIGGER_RISING | + IRQF_NO_SUSPEND, irq_name, info); + if (ret < 0) { + RT5033_FLED_ERR("Failed to request IRQ (%s): #%d: %d\n", irq_name, irq, ret); + goto err_irq; + } + } + + return 0; +err_irq: + for (j = 0; j < i; j++) { + irq_name = rt5033_get_irq_name_by_index(irq_handler[j].irq_index); + irq = platform_get_irq_byname(pdev, irq_name); + free_irq(irq, info); + } + return ret; +} + +static void unregister_irq(struct platform_device *pdev, + rt5033_fled_info_t *info) +{ + int irq; + int i; + const char *irq_name; + const struct rt5033_fled_irq_handler *irq_handler = rt5033_fled_irq_handlers; + for (i = 0; i < ARRAY_SIZE(rt5033_fled_irq_handlers); i++) { + irq_name = rt5033_get_irq_name_by_index(irq_handler[i].irq_index); + irq = platform_get_irq_byname(pdev, irq_name); + free_irq(irq, info); + } +} + + +#ifdef CONFIG_OF +static int rt5033_fled_parse_dt(struct device *dev, + struct rt5033_fled_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + u32 buffer[2]; + + /* copy default value */ + *pdata = rt5033_default_fled_pdata; + + if (of_property_read_u32_array(np, "enable", buffer, 2) == 0) { + dev_info(dev, "enable = <%d %d>\n", buffer[0], buffer[1]); + pdata->fled1_en = buffer[0]; + pdata->fled2_en = buffer[1]; + } + if (of_property_read_u32_array(np, "mid_track_alive", buffer, 1) == 0) { + dev_info(dev, "mid_track_alive = <%d>\n", buffer[0]); + pdata->fled_mid_track_alive = buffer[0]; + } + + if (of_property_read_u32_array(np, "mid_auto_track_en", buffer, 1) == 0) { + dev_info(dev, "mid_auto_track_en = <%d>\n", buffer[0]); + pdata->fled_mid_auto_track_en = buffer[0]; + } + + if (of_property_read_u32_array(np, "timeout_current_level", buffer, 1) + == 0) { + dev_info(dev, "timeout_current_level = <%d>\n", buffer[0]); + pdata->fled_timeout_current_level = RT5033_TIMEOUT_LEVEL(buffer[0]); + } + + if (of_property_read_u32_array(np, "strobe_current", buffer, 1) == 0) { + dev_info(dev, "strobe_current = <%d>\n", buffer[0]); + pdata->fled_strobe_current = RT5033_STROBE_CURRENT(buffer[0]); + } + + if (of_property_read_u32_array(np, "strobe_timeout", buffer, 1) == 0) { + dev_info(dev, "strobe_timeout = <%d>\n", buffer[0]); + pdata->fled_strobe_timeout = RT5033_STROBE_TIMEOUT(buffer[0]); + } + + if (of_property_read_u32_array(np, "torch_current", buffer, 1) == 0) { + dev_info(dev, "torch_current = <%d>\n", buffer[0]); + pdata->fled_torch_current = RT5033_TORCH_CURRENT(buffer[0]); + } + + if (of_property_read_u32_array(np, "lv_protection", buffer, 1) == 0) { + dev_info(dev, "lv_protection = <%d>\n", buffer[0]); + pdata->fled_lv_protection = RT5033_LV_PROTECTION(buffer[0]); + } + + if (of_property_read_u32_array(np, "mid_level", buffer, 1) == 0) { + dev_info(dev, "mid_level = <%d>\n", buffer[0]); + pdata->fled_mid_level = RT5033_MID_REGULATION_LEVEL(buffer[0]); + } + return 0; +} + +static struct of_device_id rt5033_fled_match_table[] = { + { .compatible = "richtek,rt5033-fled",}, + {}, +}; +#else +static int rt5033_fled_parse_dt(struct device *dev, + struct rt5033_fled_platform_data *pdata) +{ + return 0; +} +#define rt5033_fled_match_table NULL +#endif + + +static int rt5033_fled_probe(struct platform_device *pdev) +{ + int ret; + struct rt5033_mfd_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct rt5033_mfd_platform_data *mfd_pdata = chip->dev->platform_data; + struct rt5033_fled_platform_data *pdata; + rt5033_fled_info_t *fled_info; + RT5033_FLED_INFO("Richtek RT5033 FlashLED driver probing...\n"); +#ifdef CONFIG_OF +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)) + if (pdev->dev.parent->of_node) { + pdev->dev.of_node = of_find_compatible_node( + of_node_get(pdev->dev.parent->of_node), NULL, + rt5033_fled_match_table[0].compatible); + } +#endif +#endif + if (pdev->dev.of_node) { + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(&pdev->dev, "Failed to allocate memory\n"); + ret = -ENOMEM; + goto err_parse_dt_nomem; + } + ret = rt5033_fled_parse_dt(&pdev->dev, pdata); + if (ret < 0) + goto err_parse_dt; + } else { + BUG_ON(mfd_pdata == NULL); + if (mfd_pdata->fled_platform_data) + pdata = mfd_pdata->fled_platform_data; + else + pdata = &rt5033_default_fled_pdata; + } + fled_info = kzalloc(sizeof(*fled_info), GFP_KERNEL); + if (!fled_info) { + ret = -ENOMEM; + goto err_fled_nomem; + } + mutex_init(&fled_info->led_lock); + fled_info->i2c_client = chip->i2c_client; + fled_info->base.init_props = &rt5033_fled_props; + fled_info->base.hal = &rt5033_fled_hal; + fled_info->pdata = pdata; + fled_info->chip = chip; + chip->fled_info = fled_info; + platform_set_drvdata(pdev, fled_info); + + rt_fled_pdev.dev.parent = &(pdev->dev); + ret = platform_device_register(&rt_fled_pdev); + if (ret < 0) + goto err_register_pdev; + ret = register_irq(pdev, fled_info); + if (ret < 0) { + RT5033_FLED_ERR("Error : can't register irq\n"); + goto err_register_irq; + + } + return 0; +err_register_irq: +err_register_pdev: + kfree(fled_info); +err_fled_nomem: +err_parse_dt: +err_parse_dt_nomem: + return ret; +} + +static int rt5033_fled_remove(struct platform_device *pdev) +{ + struct rt5033_fled_info *fled_info; + RT5033_FLED_INFO("Richtek RT5033 FlashLED driver removing...\n"); + fled_info = platform_get_drvdata(pdev); + unregister_irq(pdev, fled_info); + platform_device_unregister(&rt_fled_pdev); + mutex_destroy(&fled_info->led_lock); + kfree(fled_info); + return 0; +} + +static struct platform_driver rt5033_fled_driver = { + .probe = rt5033_fled_probe, + .remove = rt5033_fled_remove, + .driver = { + .name = "rt5033-fled", + .owner = THIS_MODULE, + .of_match_table = rt5033_fled_match_table, + }, +}; + + +static int __init rt5033_fled_module_init(void) +{ + return platform_driver_register(&rt5033_fled_driver); +} + +static void __exit rt5033_fled_module_exit(void) +{ + platform_driver_unregister(&rt5033_fled_driver); +} + +device_initcall(rt5033_fled_module_init); +module_exit(rt5033_fled_module_exit); + +MODULE_DESCRIPTION("Richtek RT5033 FlashLED Driver"); +MODULE_AUTHOR("Patrick Chang <patrick_chang@richtek.com>"); +MODULE_VERSION(RT5033_DRV_VER); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rt5033-flashLED"); |
