Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d737fd51 authored by Subbaraman Narayanamurthy's avatar Subbaraman Narayanamurthy
Browse files

leds: qpnp-flash-v2: add support for symmetry configuration



On some HW platforms, flash LED channels grouped under a switch
device can be physically connected together. If a switch device
controls multiple flash LED channels and a non-zero brightness is
set only on one of the torch/flash LED device under it, then the
other LED channel might get internally pull down. This can end
up in an open LED fault condition on the flash LED channel where
a non-zero brightness is set.

As per the hardware recommendation, configure the brightness or
current configured on one of the flash/torch device symmetrically
on all the flash LED channels by splitting it.

CRs-Fixed: 2174782
Change-Id: I302c6a9dc7bd7523613b6c6032c7edb77b1e5584
Signed-off-by: default avatarSubbaraman Narayanamurthy <subbaram@codeaurora.org>
parent c9738304
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -182,6 +182,10 @@ Optional properties:
				  be edge triggered. Otherwise, it is level triggered.
- qcom,hw-strobe-active-low	: Boolean property to select strobe signal polarity. If defined, hw-strobe
				  signal polarity is set to active-low, else it is active-high.
- qcom,symmetry-en	: Boolean property to specify if the flash LEDs under a
			  switch node are controlled symmetrically. This needs
			  to be specified if a group of flash LED channels are
			  connected to a single LED.
Example:
	qcom,leds@d300 {
		compatible = "qcom,qpnp-flash-led-v2";
@@ -302,6 +306,7 @@ Example:
			qcom,led-mask = <3>;
			qcom,default-led-trigger =
						"switch0_trigger";
			qcom,symmetry-en;
		};

		pmi8998_switch1: qcom,led_switch_1 {
+79 −1
Original line number Diff line number Diff line
@@ -171,6 +171,7 @@ enum flash_charger_mitigation {
};

enum flash_led_type {
	FLASH_LED_TYPE_UNKNOWN,
	FLASH_LED_TYPE_FLASH,
	FLASH_LED_TYPE_TORCH,
};
@@ -204,13 +205,13 @@ struct flash_node_data {
	int				prev_current_ma;
	u8				duration;
	u8				id;
	u8				type;
	u8				ires_idx;
	u8				default_ires_idx;
	u8				hdrm_val;
	u8				current_reg_val;
	u8				strobe_ctrl;
	u8				strobe_sel;
	enum flash_led_type		type;
	bool				led_on;
};

@@ -225,6 +226,7 @@ struct flash_switch_data {
	int				led_mask;
	bool				regulator_on;
	bool				enabled;
	bool				symmetry_en;
};

/*
@@ -1091,6 +1093,71 @@ static int qpnp_flash_led_switch_disable(struct flash_switch_data *snode)
	return 0;
}

static int qpnp_flash_led_symmetry_config(struct flash_switch_data *snode)
{
	struct qpnp_flash_led *led = dev_get_drvdata(&snode->pdev->dev);
	int i, total_curr_ma = 0, num_leds = 0, prgm_current_ma;
	enum flash_led_type type = FLASH_LED_TYPE_UNKNOWN;

	for (i = 0; i < led->num_fnodes; i++) {
		if (snode->led_mask & BIT(led->fnode[i].id)) {
			if (led->fnode[i].type == FLASH_LED_TYPE_FLASH &&
				led->fnode[i].led_on)
				type = FLASH_LED_TYPE_FLASH;

			if (led->fnode[i].type == FLASH_LED_TYPE_TORCH &&
				led->fnode[i].led_on)
				type = FLASH_LED_TYPE_TORCH;
		}
	}

	if (type == FLASH_LED_TYPE_UNKNOWN) {
		pr_err("Incorrect type possibly because of no active LEDs\n");
		return -EINVAL;
	}

	for (i = 0; i < led->num_fnodes; i++) {
		if ((snode->led_mask & BIT(led->fnode[i].id)) &&
			(led->fnode[i].type == type)) {
			total_curr_ma += led->fnode[i].current_ma;
			num_leds++;
		}
	}

	if (num_leds > 0 && total_curr_ma > 0) {
		prgm_current_ma = total_curr_ma / num_leds;
	} else {
		pr_err("Incorrect configuration, num_leds: %d total_curr_ma: %d\n",
			num_leds, total_curr_ma);
		return -EINVAL;
	}

	if (prgm_current_ma == 0) {
		pr_warn("prgm_curr_ma cannot be 0\n");
		return 0;
	}

	pr_debug("num_leds: %d total: %d prgm_curr_ma: %d\n", num_leds,
		total_curr_ma, prgm_current_ma);

	for (i = 0; i < led->num_fnodes; i++) {
		if (snode->led_mask & BIT(led->fnode[i].id) &&
			led->fnode[i].current_ma != prgm_current_ma &&
			led->fnode[i].type == type) {
			qpnp_flash_led_node_set(&led->fnode[i],
				prgm_current_ma);
			pr_debug("%s LED %d current: %d code: %d ires_ua: %d\n",
				(type == FLASH_LED_TYPE_FLASH) ?
					"flash" : "torch",
				led->fnode[i].id, prgm_current_ma,
				led->fnode[i].current_reg_val,
				led->fnode[i].ires_ua);
		}
	}

	return 0;
}

static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
{
	struct qpnp_flash_led *led = dev_get_drvdata(&snode->pdev->dev);
@@ -1109,6 +1176,15 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
	}

	/* Iterate over all active leds for this switch node */
	if (snode->symmetry_en) {
		rc = qpnp_flash_led_symmetry_config(snode);
		if (rc < 0) {
			pr_err("Failed to configure current symmetrically, rc=%d\n",
				rc);
			return rc;
		}
	}

	val = 0;
	for (i = 0; i < led->num_fnodes; i++)
		if (led->fnode[i].led_on &&
@@ -1707,6 +1783,8 @@ static int qpnp_flash_led_parse_and_register_switch(struct qpnp_flash_led *led,
		return rc;
	}

	snode->symmetry_en = of_property_read_bool(node, "qcom,symmetry-en");

	if (snode->led_mask < 1 || snode->led_mask > 7) {
		pr_err("Invalid value for led-mask\n");
		return -EINVAL;