led: leds-qpnp-flash: change flash LED enablement method

Current flash LED enablement method glows LEDs one after another.
When it comes with battery current limit, two consecutive battery
level query causes inaccurate reading and delay in enablement,
which is not tolerable to camera and PMIC. In order to overcome
this issue, a new node is introduced as a switch, to provide
capability to glow both LEDs together. This addresses these
issues.

CRs-Fixed: 793302
Change-Id: I7e1689cc521f145bcfa3519c6f21e93d907f3408
Signed-off-by: Jigarkumar Kishorkumar Zala <j_zala@codeaurora.org>
This commit is contained in:
Chun Zhang 2015-02-18 11:07:57 -08:00 committed by Jigarkumar Kishorkumar Zala
parent 8040f3bfca
commit 3128b25249
2 changed files with 386 additions and 171 deletions

View File

@ -54,8 +54,15 @@ Optional properties:
when battery voltage is low
-qcom,otst2-module-enabled : Boolean type. This enables driver to enable MASK to support
OTST2 connection.
- qcom,duration : duration for flash LED. When duration time expires, hardware
will turn off flash LED. Values should be from 10 ms to 1280 ms
with 10 ms incremental step. Not applicable to torch.
Required properties inside child node. Chile node contains settings for each individual LED:
Required properties inside child node. Child node contains settings for each individual LED.
Each LED hardware needs a node for itself and a switch node to control brightness.
For the purpose of turning on/off LED and better regulator control, "led:switch" node
is introduced. "led:switch" acquires several existing properties from other nodes for
operational simplification. For backward compatibility purpose, swicth node can be optional:
- label : type of led that will be used, either "flash" or "torch".
- qcom,led-name : name of the LED. Accepted values are "led:flash_0",
"led:flash_1", "led:torch_0", "led:torch_1"
@ -64,17 +71,13 @@ Required properties inside child node. Chile node contains settings for each ind
- qcom,id : enumerated ID for each physical LED. Accepted values are "0",
"1", etc..
- qcom,max-current : maximum current allowed on this LED. Valid values should be
integer from 0 to 1000 inclusive, indicating 0 to 1000 mA.
integer from 0 to 1000 inclusive, indicating 0 to 1000 mA.
Optional properties inside child node:
- qcom,current : default current intensity for LED. Accepted values should be
integer from 0 t 1000 inclusive, indicating 0 to 1000 mA.
- boost-supply : flash LED boost power source for flash LED
- boost-voltage-max : maximum voltage for flash LED boost regulator in uV. This attribute is
: required if boost-supply is defined.
- qcom,duration : duration for flash LED. When duration time expires, hardware
will turn off flash LED. Values should be from 10 ms to 1280 ms
with 10 ms incremental step. Not applicable to torch.
Example:
qcom,leds@d300 {
@ -98,17 +101,16 @@ Example:
qcom,vph-pwr-droop-debounce-time = <10>;
qcom,headroom-sense-ch0-enabled;
qcom,headroom-sense-ch1-enabled;
qcom,duration = <1280>;
pm8226_flash0: qcom,flash_0 {
label = "flash";
qcom,led-name = "led:flash_0";
qcom,default-led-trigger =
"flash0_trigger";
qcom,max-current = <1000>;
qcom,duration = <1280>;
qcom,id = <0>;
qcom,current = <625>;
boost-supply = <&pm8226_chg_boost>;
qcom,max-current = <1000>;
};
pm8226_torch: qcom,torch_0 {
@ -116,10 +118,20 @@ Example:
qcom,led-name = "led:torch_0";
qcom,default-led-trigger =
"torch0_trigger";
boost-supply = <&pm8226_chg_boost>;
qcom,max-current = <200>;
qcom,id = <0>;
qcom,current = <120>;
qcom,max-current = <200>;
};
pm8226_switch: qcom,switch {
label = "switch";
qcom,led-name = "led:switch";
qcom,default-led-triffer =
"switch_trigger";
boost-supply = <&pm8226_chg_boost>;
qcom,id = <2>;
qcom,current = <625>;
qcom,max-current = <1000>;
boost-voltage-max = <3600000>;
};
};

View File

@ -96,6 +96,9 @@
#define FLASH_LED_UA_PER_MA 1000
#define FLASH_LED_MASK_MODULE_MASK2_ENABLE 0x20
#define FLASH_LED_MASK3_ENABLE_SHIFT 7
#define FLASH_LED_MODULE_CTRL_DEFAULT 0x60
#define FLASH_LED_CURRENT_READING_DELAY_MIN 5000
#define FLASH_LED_CURRENT_READING_DELAY_MAX 5001
#define FLASH_UNLOCK_SECURE 0xA5
#define FLASH_LED_TORCH_ENABLE 0x00
@ -116,11 +119,13 @@
enum flash_led_id {
FLASH_LED_0 = 0,
FLASH_LED_1,
FLASH_LED_SWITCH,
};
enum flash_led_type {
FLASH = 0,
TORCH,
SWITCH,
};
enum thermal_derate_rate {
@ -152,9 +157,7 @@ struct flash_node_data {
struct work_struct work;
struct delayed_work dwork;
u32 boost_voltage_max;
u16 duration;
u16 max_current;
u16 current_addr;
u16 prgm_current;
u8 id;
u8 type;
@ -172,6 +175,7 @@ struct flash_led_platform_data {
u16 vph_pwr_droop_threshold;
u16 headroom;
u16 clamp_current;
u16 duration;
u8 thermal_derate_threshold;
u8 vph_pwr_droop_debounce_time;
u8 startup_dly;
@ -201,15 +205,44 @@ struct qpnp_flash_led {
struct mutex flash_led_lock;
int num_leds;
u16 base;
u16 current_addr;
u16 current2_addr;
u8 peripheral_type;
bool gpio_enabled;
bool charging_enabled;
};
static u8 qpnp_flash_led_ctrl_dbg_regs[] = {
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x4A, 0x4B, 0x4F, 0x51, 0x52, 0x54, 0x55, 0x5A
0x4A, 0x4B, 0x4C, 0x4F, 0x51, 0x52, 0x54, 0x55, 0x5A
};
static int
qpnp_led_masked_write(struct spmi_device *spmi_dev, u16 addr, u8 mask, u8 val)
{
int rc;
u8 reg;
rc = spmi_ext_register_readl(spmi_dev->ctrl, spmi_dev->sid,
addr, &reg, 1);
if (rc)
dev_err(&spmi_dev->dev,
"Unable to read from addr=%x, rc(%d)\n", addr, rc);
reg &= ~mask;
reg |= val;
rc = spmi_ext_register_writel(spmi_dev->ctrl, spmi_dev->sid,
addr, &reg, 1);
if (rc)
dev_err(&spmi_dev->dev,
"Unable to write to addr=%x, rc(%d)\n", addr, rc);
dev_dbg(&spmi_dev->dev, "Write 0x%02X to addr 0x%02X\n", val, addr);
return rc;
}
static int
qpnp_flash_led_get_max_avail_current(struct flash_node_data *flash_node,
struct qpnp_flash_led *led)
@ -218,31 +251,40 @@ qpnp_flash_led_get_max_avail_current(struct flash_node_data *flash_node,
int max_curr_avail_ma;
int rc;
if (led->battery_psy) {
rc = led->battery_psy->get_property(led->battery_psy,
POWER_SUPPLY_PROP_FLASH_CURRENT_MAX,
&prop);
if (!led->battery_psy) {
dev_err(&led->spmi_dev->dev,
"Failed to query power supply\n");
return -EINVAL;
}
/*
* When charging is enabled, enforce this new
* enabelment sequence to reduce fuel gauge
* resolution reading.
*/
if (led->charging_enabled) {
rc = qpnp_led_masked_write(led->spmi_dev,
FLASH_MODULE_ENABLE_CTRL(led->base),
FLASH_MODULE_ENABLE, FLASH_MODULE_ENABLE);
if (rc) {
dev_err(&led->spmi_dev->dev,
"Failed to get power supply property\n");
"Module enable reg write failed\n");
return -EINVAL;
}
if (!prop.intval) {
dev_err(&led->spmi_dev->dev,
"battery too low for flash\n");
return 0;
}
} else {
usleep_range(FLASH_LED_CURRENT_READING_DELAY_MIN,
FLASH_LED_CURRENT_READING_DELAY_MAX);
}
led->battery_psy->get_property(led->battery_psy,
POWER_SUPPLY_PROP_FLASH_CURRENT_MAX, &prop);
if (!prop.intval) {
dev_err(&led->spmi_dev->dev,
"failed to query power supply battery device\n");
"battery too low for flash\n");
return -EINVAL;
}
max_curr_avail_ma = (int)(prop.intval / FLASH_LED_UA_PER_MA);
max_curr_avail_ma = max_curr_avail_ma / 2;
if (max_curr_avail_ma > flash_node->max_current)
max_curr_avail_ma = flash_node->max_current;
max_curr_avail_ma = (prop.intval / FLASH_LED_UA_PER_MA);
return max_curr_avail_ma;
}
@ -368,32 +410,6 @@ static struct device_attribute qpnp_flash_led_attrs[] = {
NULL),
};
static int
qpnp_led_masked_write(struct spmi_device *spmi_dev, u16 addr, u8 mask, u8 val)
{
int rc;
u8 reg;
rc = spmi_ext_register_readl(spmi_dev->ctrl, spmi_dev->sid,
addr, &reg, 1);
if (rc)
dev_err(&spmi_dev->dev,
"Unable to read from addr=%x, rc(%d)\n", addr, rc);
reg &= ~mask;
reg |= val;
rc = spmi_ext_register_writel(spmi_dev->ctrl, spmi_dev->sid,
addr, &reg, 1);
if (rc)
dev_err(&spmi_dev->dev,
"Unable to write to addr=%x, rc(%d)\n", addr, rc);
dev_dbg(&spmi_dev->dev, "Write 0x%02X to addr 0x%02X\n", val, addr);
return rc;
}
static int qpnp_flash_led_get_thermal_derate_rate(const char *rate)
{
/*
@ -516,29 +532,28 @@ static int qpnp_flash_led_module_disable(struct qpnp_flash_led *led,
tmp = ~flash_node->trigger & val;
if (!tmp) {
if (flash_node->type == TORCH) {
rc = qpnp_led_masked_write(led->spmi_dev,
rc = qpnp_led_masked_write(led->spmi_dev,
FLASH_LED_UNLOCK_SECURE(led->base),
FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
if (rc) {
dev_err(&led->spmi_dev->dev,
"Secure reg write failed\n");
return -EINVAL;
}
if (rc) {
dev_err(&led->spmi_dev->dev,
"Secure reg write failed\n");
return -EINVAL;
}
rc = qpnp_led_masked_write(led->spmi_dev,
rc = qpnp_led_masked_write(led->spmi_dev,
FLASH_TORCH(led->base),
FLASH_TORCH_MASK, FLASH_LED_TORCH_DISABLE);
if (rc) {
dev_err(&led->spmi_dev->dev,
"Torch reg write failed\n");
return -EINVAL;
}
if (rc) {
dev_err(&led->spmi_dev->dev,
"Torch reg write failed\n");
return -EINVAL;
}
rc = qpnp_led_masked_write(led->spmi_dev,
FLASH_MODULE_ENABLE_CTRL(led->base),
FLASH_MODULE_ENABLE_MASK, FLASH_LED_DISABLE);
FLASH_MODULE_ENABLE_MASK,
FLASH_LED_MODULE_CTRL_DEFAULT);
if (rc) {
dev_err(&led->spmi_dev->dev, "Module disable failed\n");
return -EINVAL;
@ -566,10 +581,35 @@ static int qpnp_flash_led_module_disable(struct qpnp_flash_led *led,
return -EINVAL;
}
}
if (!led->flash_node[0].flash_on &&
!led->flash_node[2].flash_on) {
rc = qpnp_led_masked_write(led->spmi_dev,
led->current_addr,
FLASH_CURRENT_MASK, 0x00);
if (rc) {
dev_err(&led->spmi_dev->dev,
"current register write failed\n");
return -EINVAL;
}
}
if (!led->flash_node[1].flash_on &&
!led->flash_node[3].flash_on) {
rc = qpnp_led_masked_write(led->spmi_dev,
led->current2_addr,
FLASH_CURRENT_MASK, 0x00);
if (rc) {
dev_err(&led->spmi_dev->dev,
"current register write failed\n");
return -EINVAL;
}
}
} else {
rc = qpnp_led_masked_write(led->spmi_dev,
FLASH_MODULE_ENABLE_CTRL(led->base),
flash_node->enable, flash_node->enable);
FLASH_MODULE_ENABLE_MASK,
FLASH_LED_MODULE_CTRL_DEFAULT);
if (rc) {
dev_err(&led->spmi_dev->dev, "Module disable failed\n");
return -EINVAL;
@ -592,7 +632,8 @@ static void qpnp_flash_led_work(struct work_struct *work)
dev_get_drvdata(&flash_node->spmi_dev->dev);
union power_supply_propval psy_prop;
int rc, brightness = flash_node->cdev.brightness;
int max_curr_avail_ma;
int max_curr_avail_ma = 0;
int i;
u8 val;
mutex_lock(&led->flash_led_lock);
@ -600,19 +641,14 @@ static void qpnp_flash_led_work(struct work_struct *work)
if (!brightness)
goto turn_off;
if (brightness < FLASH_LED_MIN_CURRENT_MA)
brightness = FLASH_LED_MIN_CURRENT_MA;
flash_node->prgm_current = brightness;
if (flash_node->boost_regulator && !flash_node->flash_on) {
if (regulator_count_voltages(flash_node->boost_regulator) > 0) {
rc = regulator_set_voltage(flash_node->boost_regulator,
flash_node->boost_voltage_max,
flash_node->boost_voltage_max);
flash_node->boost_voltage_max,
flash_node->boost_voltage_max);
if (rc) {
dev_err(&led->spmi_dev->dev,
"boost regulator set voltage failed\n");
"boost regulator set voltage failed\n");
mutex_unlock(&led->flash_led_lock);
return;
}
@ -637,7 +673,11 @@ static void qpnp_flash_led_work(struct work_struct *work)
led->gpio_enabled = true;
}
if (flash_node->type == TORCH) {
if (((led->flash_node[2].flash_on ||
led->flash_node[3].flash_on) &&
!led->flash_node[0].flash_on &&
!led->flash_node[1].flash_on) ||
flash_node->type == TORCH) {
rc = qpnp_led_masked_write(led->spmi_dev,
FLASH_LED_UNLOCK_SECURE(led->base),
FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
@ -655,16 +695,59 @@ static void qpnp_flash_led_work(struct work_struct *work)
"Torch reg write failed\n");
goto exit_flash_led_work;
}
if (led->flash_node[led->num_leds - 1].id ==
FLASH_LED_SWITCH) {
if (led->flash_node[2].flash_on) {
val = (u8)(led->flash_node[2].prgm_current *
FLASH_TORCH_MAX_LEVEL
/ led->flash_node[2].max_current);
rc = qpnp_led_masked_write(led->spmi_dev,
led->current_addr,
FLASH_CURRENT_MASK, val);
if (rc) {
dev_err(&led->spmi_dev->dev,
"Torch reg write failed\n");
goto exit_flash_led_work;
}
}
val = (u8)(flash_node->prgm_current * FLASH_TORCH_MAX_LEVEL
/ flash_node->max_current);
rc = qpnp_led_masked_write(led->spmi_dev,
flash_node->current_addr,
FLASH_CURRENT_MASK, val);
if (rc) {
dev_err(&led->spmi_dev->dev,
"Current reg write failed\n");
goto exit_flash_led_work;
if (led->flash_node[3].flash_on) {
val = (u8)(flash_node[3].prgm_current *
FLASH_TORCH_MAX_LEVEL
/ led->flash_node[3].max_current);
rc = qpnp_led_masked_write(led->spmi_dev,
led->current2_addr,
FLASH_CURRENT_MASK, val);
if (rc) {
dev_err(&led->spmi_dev->dev,
"Torch reg write failed\n");
goto exit_flash_led_work;
}
}
} else {
val = (u8)(flash_node->prgm_current *
FLASH_TORCH_MAX_LEVEL /
flash_node->max_current);
if (flash_node->id == FLASH_LED_0) {
rc = qpnp_led_masked_write(led->spmi_dev,
led->current_addr,
FLASH_CURRENT_MASK, val);
if (rc) {
dev_err(&led->spmi_dev->dev,
"current reg write failed\n");
goto exit_flash_led_work;
}
} else {
rc = qpnp_led_masked_write(led->spmi_dev,
led->current2_addr,
FLASH_CURRENT_MASK, val);
if (rc) {
dev_err(&led->spmi_dev->dev,
"current reg write failed\n");
goto exit_flash_led_work;
}
}
}
rc = qpnp_led_masked_write(led->spmi_dev,
@ -678,8 +761,7 @@ static void qpnp_flash_led_work(struct work_struct *work)
rc = qpnp_led_masked_write(led->spmi_dev,
FLASH_MODULE_ENABLE_CTRL(led->base),
FLASH_MODULE_ENABLE | flash_node->enable,
FLASH_MODULE_ENABLE | flash_node->enable);
FLASH_MODULE_ENABLE_MASK, FLASH_MODULE_ENABLE);
if (rc) {
dev_err(&led->spmi_dev->dev,
"Module enable reg write failed\n");
@ -695,14 +777,15 @@ static void qpnp_flash_led_work(struct work_struct *work)
"Strobe ctrl reg write failed\n");
goto exit_flash_led_work;
}
} else if (flash_node->type == FLASH) {
if (!led->battery_psy)
led->battery_psy = power_supply_get_by_name("battery");
if (!led->battery_psy) {
dev_err(&led->spmi_dev->dev,
"Failed to get battery power supply\n");
goto exit_flash_led_work;
}
} else if (((led->flash_node[0].flash_on ||
led->flash_node[1].flash_on) &&
!led->flash_node[2].flash_on &&
!led->flash_node[3].flash_on) ||
flash_node->type == FLASH) {
if (led->flash_node[0].flash_on)
max_curr_avail_ma += led->flash_node[0].max_current;
if (led->flash_node[1].flash_on)
max_curr_avail_ma += led->flash_node[1].max_current;
psy_prop.intval = true;
rc = led->battery_psy->set_property(led->battery_psy,
@ -715,25 +798,128 @@ static void qpnp_flash_led_work(struct work_struct *work)
}
if (led->pdata->power_detect_en) {
max_curr_avail_ma =
qpnp_flash_led_get_max_avail_current
(flash_node, led);
if (max_curr_avail_ma < 0) {
dev_err(&led->spmi_dev->dev,
"Failed to get Max available curr\n");
goto exit_flash_led_work;
} else {
if (max_curr_avail_ma <
flash_node->prgm_current) {
if (led->battery_psy) {
led->battery_psy->get_property(led->battery_psy,
POWER_SUPPLY_PROP_STATUS,
&psy_prop);
if (psy_prop.intval < 0) {
dev_err(&led->spmi_dev->dev,
"battery only supports %d mA.\n",
max_curr_avail_ma);
flash_node->prgm_current =
(u16) max_curr_avail_ma;
"Invalid battery status\n");
goto exit_flash_led_work;
}
if (psy_prop.intval ==
POWER_SUPPLY_STATUS_CHARGING)
led->charging_enabled = true;
else if (psy_prop.intval ==
POWER_SUPPLY_STATUS_DISCHARGING
|| psy_prop.intval ==
POWER_SUPPLY_STATUS_NOT_CHARGING)
led->charging_enabled = false;
max_curr_avail_ma =
qpnp_flash_led_get_max_avail_current
(flash_node, led);
if (max_curr_avail_ma < 0) {
dev_err(&led->spmi_dev->dev,
"Failed to get max avail curr\n");
goto exit_flash_led_work;
}
}
}
val = (u8)((flash_node->duration - FLASH_DURATION_DIVIDER)
if (led->flash_node[led->num_leds - 1].id ==
FLASH_LED_SWITCH) {
if (led->flash_node[0].flash_on &&
led->flash_node[1].flash_on) {
if (max_curr_avail_ma <
flash_node->prgm_current) {
led->flash_node[0].prgm_current =
led->flash_node[0].prgm_current
* max_curr_avail_ma /
flash_node->prgm_current;
led->flash_node[1].prgm_current =
led->flash_node[1].prgm_current
* max_curr_avail_ma /
flash_node->prgm_current;
}
} else {
if (led->flash_node[0].flash_on) {
led->flash_node[0].prgm_current
= (max_curr_avail_ma <
led->flash_node[0].prgm_current)
? max_curr_avail_ma :
led->flash_node[0].prgm_current;
}
if (led->flash_node[1].flash_on) {
led->flash_node[1].prgm_current
= (max_curr_avail_ma <
led->flash_node[1].prgm_current)
? max_curr_avail_ma :
led->flash_node[1].prgm_current;
}
}
val = (u8)(led->flash_node[0].prgm_current *
FLASH_MAX_LEVEL /
led->flash_node[0].max_current);
rc = qpnp_led_masked_write(led->spmi_dev,
led->current_addr,
FLASH_CURRENT_MASK, val);
if (rc) {
dev_err(&led->spmi_dev->dev,
"Current register write failed\n");
goto exit_flash_led_work;
}
val = (u8)(led->flash_node[1].prgm_current *
FLASH_MAX_LEVEL /
led->flash_node[1].max_current);
rc = qpnp_led_masked_write(led->spmi_dev,
led->current2_addr,
FLASH_CURRENT_MASK, val);
if (rc) {
dev_err(&led->spmi_dev->dev,
"Current register write failed\n");
goto exit_flash_led_work;
}
} else {
if (max_curr_avail_ma <
flash_node->prgm_current) {
dev_err(&led->spmi_dev->dev,
"battery only supprots %d mA\n",
max_curr_avail_ma);
flash_node->prgm_current =
(u16)max_curr_avail_ma;
}
val = (u8)(flash_node->prgm_current *
FLASH_MAX_LEVEL
/ flash_node->max_current);
if (flash_node->id == FLASH_LED_0) {
rc = qpnp_led_masked_write(
led->spmi_dev,
led->current_addr,
FLASH_CURRENT_MASK, val);
if (rc) {
dev_err(&led->spmi_dev->dev,
"current reg write failed\n");
goto exit_flash_led_work;
}
} else if (flash_node->id == FLASH_LED_1) {
rc = qpnp_led_masked_write(
led->spmi_dev,
led->current2_addr,
FLASH_CURRENT_MASK, val);
if (rc) {
dev_err(&led->spmi_dev->dev,
"current reg write failed\n");
goto exit_flash_led_work;
}
}
}
val = (u8)((led->pdata->duration - FLASH_DURATION_DIVIDER)
/ FLASH_DURATION_DIVIDER);
rc = qpnp_led_masked_write(led->spmi_dev,
FLASH_SAFETY_TIMER(led->base),
@ -753,30 +939,18 @@ static void qpnp_flash_led_work(struct work_struct *work)
goto exit_flash_led_work;
}
val = (u8)(flash_node->prgm_current * FLASH_MAX_LEVEL
/ flash_node->max_current);
rc = qpnp_led_masked_write(led->spmi_dev,
flash_node->current_addr,
FLASH_CURRENT_MASK, val);
if (rc) {
dev_err(&led->spmi_dev->dev,
"Current reg write failed\n");
goto exit_flash_led_work;
}
rc = qpnp_led_masked_write(led->spmi_dev,
if (!led->charging_enabled) {
rc = qpnp_led_masked_write(led->spmi_dev,
FLASH_MODULE_ENABLE_CTRL(led->base),
FLASH_MODULE_ENABLE |
flash_node->enable,
FLASH_MODULE_ENABLE |
flash_node->enable);
if (rc) {
dev_err(&led->spmi_dev->dev,
"Module enable reg write failed\n");
goto exit_flash_led_work;
}
FLASH_MODULE_ENABLE, FLASH_MODULE_ENABLE);
if (rc) {
dev_err(&led->spmi_dev->dev,
"Module enable reg write failed\n");
goto exit_flash_led_work;
}
usleep(FLASH_RAMP_UP_DELAY_US);
usleep(FLASH_RAMP_UP_DELAY_US);
}
rc = qpnp_led_masked_write(led->spmi_dev,
FLASH_LED_STROBE_CTRL(led->base),
@ -787,6 +961,11 @@ static void qpnp_flash_led_work(struct work_struct *work)
"Strobe reg write failed\n");
goto exit_flash_led_work;
}
} else {
pr_err("Both Torch and Flash cannot be select at same time\n");
for (i = 0; i < led->num_leds; i++)
led->flash_node[i].flash_on = false;
goto turn_off;
}
flash_node->flash_on = true;
@ -830,8 +1009,10 @@ static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct flash_node_data *flash_node;
struct qpnp_flash_led *led;
flash_node = container_of(led_cdev, struct flash_node_data, cdev);
led = dev_get_drvdata(&flash_node->spmi_dev->dev);
if (value < LED_OFF) {
pr_err("Invalid brightness value\n");
return;
@ -841,10 +1022,33 @@ static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev,
value = flash_node->cdev.max_brightness;
flash_node->cdev.brightness = value;
schedule_work(&flash_node->work);
return;
if (led->flash_node[led->num_leds - 1].id ==
FLASH_LED_SWITCH) {
if (flash_node->id == FLASH_LED_0 ||
flash_node->id == FLASH_LED_1) {
if (value < FLASH_LED_MIN_CURRENT_MA && value != 0)
value = FLASH_LED_MIN_CURRENT_MA;
flash_node->prgm_current = value;
flash_node->flash_on = value ? true : false;
return;
} else if (flash_node->id == FLASH_LED_SWITCH) {
if (led->flash_node[0].flash_on ||
led->flash_node[1].flash_on)
flash_node->prgm_current =
led->flash_node[0].prgm_current +
led->flash_node[1].prgm_current;
flash_node->trigger = FLASH_LED0_TRIGGER |
FLASH_LED1_TRIGGER;
schedule_work(&flash_node->work);
return;
}
} else {
if (value < FLASH_LED_MIN_CURRENT_MA && value != 0)
value = FLASH_LED_MIN_CURRENT_MA;
flash_node->prgm_current = value;
schedule_work(&flash_node->work);
return;
}
}
static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led)
@ -854,7 +1058,8 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led)
rc = qpnp_led_masked_write(led->spmi_dev,
FLASH_MODULE_ENABLE_CTRL(led->base),
FLASH_MODULE_ENABLE_MASK, FLASH_LED_DISABLE);
FLASH_MODULE_ENABLE_MASK,
FLASH_LED_MODULE_CTRL_DEFAULT);
if (rc) {
dev_err(&led->spmi_dev->dev, "Module disable failed\n");
return rc;
@ -1026,6 +1231,13 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led)
}
}
led->battery_psy = power_supply_get_by_name("battery");
if (!led->battery_psy) {
dev_err(&led->spmi_dev->dev,
"Failed to get battery power supply\n");
return -EINVAL;
}
return 0;
}
@ -1065,6 +1277,8 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
flash_node->type = FLASH;
else if (strcmp(temp_string, "torch") == 0)
flash_node->type = TORCH;
else if (strcmp(temp_string, "switch") == 0)
flash_node->type = SWITCH;
else {
dev_err(&led->spmi_dev->dev,
"Wrong flash LED type\n");
@ -1080,18 +1294,10 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
if (!rc) {
if (val < FLASH_LED_MIN_CURRENT_MA)
val = FLASH_LED_MIN_CURRENT_MA;
flash_node->prgm_current = (u16)val;
flash_node->prgm_current = val;
} else if (rc != -EINVAL) {
dev_err(&led->spmi_dev->dev,
"Unable to read current settings\n");
return rc;
}
rc = of_property_read_u32(node, "qcom,duration", &val);
if (!rc)
flash_node->duration = (u16)val;
else if (rc != -EINVAL) {
dev_err(&led->spmi_dev->dev, "Unable to read clamp current\n");
"Unable to read current\n");
return rc;
}
@ -1105,24 +1311,12 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
switch (led->peripheral_type) {
case FLASH_SUBTYPE_SINGLE:
flash_node->current_addr = FLASH_LED0_CURRENT(led->base);
flash_node->enable = FLASH_LED0_ENABLEMENT;
flash_node->trigger = FLASH_LED0_TRIGGER;
break;
case FLASH_SUBTYPE_DUAL:
if (flash_node->id == FLASH_LED_0) {
flash_node->enable = FLASH_LED0_ENABLEMENT;
if (flash_node->type == TORCH)
flash_node->enable = FLASH_MODULE_ENABLE;
flash_node->current_addr =
FLASH_LED0_CURRENT(led->base);
flash_node->trigger = FLASH_LED0_TRIGGER;
} else if (flash_node->id == FLASH_LED_1) {
flash_node->enable = FLASH_LED1_ENABLEMENT;
if (flash_node->type == TORCH)
flash_node->enable = FLASH_MODULE_ENABLE;
flash_node->current_addr =
FLASH_LED1_CURRENT(led->base);
flash_node->trigger = FLASH_LED1_TRIGGER;
}
break;
@ -1197,6 +1391,15 @@ static int qpnp_flash_led_parse_common_dt(
return rc;
}
rc = of_property_read_u32(node, "qcom,duration", &val);
if (!rc) {
led->pdata->duration = val;
} else if (rc != -EINVAL) {
dev_err(&led->spmi_dev->dev,
"unable to read duration\n");
return rc;
}
led->pdata->pmic_charger_support =
of_property_read_bool(node,
"qcom,pmic-charger-support");
@ -1379,6 +1582,8 @@ static int qpnp_flash_led_probe(struct spmi_device *spmi)
led->base = flash_resource->start;
led->spmi_dev = spmi;
led->current_addr = FLASH_LED0_CURRENT(led->base);
led->current2_addr = FLASH_LED1_CURRENT(led->base);
led->pdata = devm_kzalloc(&spmi->dev,
sizeof(struct flash_led_platform_data), GFP_KERNEL);
@ -1455,12 +1660,11 @@ static int qpnp_flash_led_probe(struct spmi_device *spmi)
val = FLASH_LED_MIN_CURRENT_MA;
led->flash_node[i].max_current = (u16)val;
led->flash_node[i].cdev.max_brightness = val;
} else if (rc < 0) {
} else {
dev_err(&led->spmi_dev->dev,
"Unable to read max current\n");
"Unable to read max current\n");
return rc;
}
rc = led_classdev_register(&spmi->dev,
&led->flash_node[i].cdev);
if (rc) {
@ -1480,7 +1684,7 @@ static int qpnp_flash_led_probe(struct spmi_device *spmi)
for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++) {
rc =
sysfs_create_file(&led->flash_node[i].cdev.dev->kobj,
&qpnp_flash_led_attrs[j].attr);
&qpnp_flash_led_attrs[j].attr);
if (rc)
goto error_led_register;
}
@ -1489,7 +1693,6 @@ static int qpnp_flash_led_probe(struct spmi_device *spmi)
}
led->num_leds = i;
dev_set_drvdata(&spmi->dev, led);
return 0;
@ -1543,7 +1746,7 @@ static int __init qpnp_flash_led_init(void)
{
return spmi_driver_register(&qpnp_flash_led_driver);
}
module_init(qpnp_flash_led_init);
late_initcall(qpnp_flash_led_init);
static void __exit qpnp_flash_led_exit(void)
{