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:
parent
8040f3bfca
commit
3128b25249
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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, ®, 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, ®, 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, ®, 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, ®, 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)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue