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

Commit 16fcc41f authored by Mohan Pallaka's avatar Mohan Pallaka
Browse files

leds: qpnp-wled: add external pfet sc support



WLED can be gated with an external pfet for
short circuit protection. Enable this based on
platform data.

Remove the support for dim shape which is not
supported by the hardware.

Change-Id: Ibd706b65c746c5e5eba31ea72dc387cf0beafe94
Signed-off-by: default avatarMohan Pallaka <mpallaka@codeaurora.org>
parent 1677ceb2
Loading
Loading
Loading
Loading
+14 −17
Original line number Diff line number Diff line
@@ -28,11 +28,9 @@ Optional properties for WLED:
- qcom,ovp-mv		: over voltage protection value in mv. default is 17800.
- qcom,ilim-ma		: maximum current limiter in ma. default is 980.
- qcom,boost-duty-ns	: maximum boost duty cycle in ns. default is 104.
- qcom,mod-freq-khz	: modulation frequency in khz. default is 19200.
- qcom,mod-freq-khz	: modulation frequency in khz. default is 9600.
- qcom,dim-mode		: dimming mode. supporting dimming modes are "analog",
			  "digital", and "hybrid". default is "hybrid".
- qcom,dim-method	: dimming method. supported dimming methods are "log", "square",
			  and "linear". default is "linear".
- qcom,hyb-thres	: threshold value when used in hybrid mode. It represents the
			  percentage of brightntess at which dimming mode is switched
			  from "digital" to "analog". the default value is 6.25%. as the
@@ -51,6 +49,7 @@ Optional properties for WLED:
- qcom,led-strings-list	: Wled module has four strings of leds numbered from 0 to 3. each string of leds
			  are operated individually. specify the list of strings used by the device.
			  any combination of led strings can be used. default value is [00 01 02 03]
- qcom,en-ext-pfet-sc-pro : Specify if external pfet short circuit protection is needed

Example:
	qcom,leds@d800 {
@@ -65,25 +64,23 @@ Example:
				"qpnp-wled-lab-base";
		interrupts = <0x3 0xd8 0x2>;
		interrupt-names = "sc-irq";
		status = "okay";
		linux,name = "wled";
		linux,default-trigger = "bkl-trigger";
		qcom,fdbk-output = "auto";
		qcom,vref-mv = <300>;
		qcom,vref-mv = <350>;
		qcom,switch-freq-khz = <800>;
		qcom,ovp-mv = <31000>;
		qcom,ilim-ma = <1980>;
		qcom,boost-duty-ns = <52>;
		qcom,mod-freq-khz = <19200>;
		qcom,ovp-mv = <29500>;
		qcom,ilim-ma = <980>;
		qcom,boost-duty-ns = <26>;
		qcom,mod-freq-khz = <9600>;
		qcom,dim-mode = "hybrid";
		qcom,dim-method = "linear";
		qcom,hyb-thres = <625>;
		qcom,sync-dly-us = <800>;
		qcom,fs-curr-ua = <30000>;
		qcom,fs-curr-ua = <16000>;
		qcom,en-phase-stag;
		qcom,en-cabc;
		qcom,led-strings-list = [00 01 02 03];
		qcom,ibb-bias-active;
		qcom,ibb-pwrup-dly = <8>;
		qcom,lab-fast-precharge;
		qcom,disp-type-amoled;
		qcom,led-strings-list = [00 01 02 03];
		qcom,en-ext-pfet-sc-pro;
	};
+48 −67
Original line number Diff line number Diff line
@@ -41,6 +41,8 @@
#define QPNP_WLED_SWITCH_FREQ_REG(b)	(b + 0x4C)
#define QPNP_WLED_OVP_REG(b)		(b + 0x4D)
#define QPNP_WLED_ILIM_REG(b)		(b + 0x4E)
#define QPNP_WLED_SC_PRO_REG(b)		(b + 0x5E)
#define QPNP_WLED_TEST_REG(b)		(b + 0xE2)

#define QPNP_WLED_EN_MASK		0x7F
#define QPNP_WLED_EN_SHIFT		7
@@ -87,6 +89,8 @@
#define QPNP_WLED_MOD_FREQ_19200_KHZ	19200
#define QPNP_WLED_MOD_FREQ_MASK		0x3F
#define QPNP_WLED_MOD_FREQ_SHIFT	6
#define QPNP_WLED_ACC_CLK_FREQ_MASK	0xE7
#define QPNP_WLED_ACC_CLK_FREQ_SHIFT	3
#define QPNP_WLED_PHASE_STAG_MASK	0xDF
#define QPNP_WLED_PHASE_STAG_SHIFT	5
#define QPNP_WLED_DIM_RES_MASK		0xFD
@@ -131,6 +135,9 @@
#define QPNP_WLED_MODULE_EN_SHIFT	7
#define QPNP_WLED_DISP_SEL_MASK		0x7F
#define QPNP_WLED_DISP_SEL_SHIFT	7
#define QPNP_WLED_EN_SC_MASK		0x7F
#define QPNP_WLED_EN_SC_SHIFT		7
#define QPNP_WLED_EXT_FET_DTEST2	0x09

#define QPNP_WLED_IBB_BIAS_REG(b)	(b + 0x58)
#define QPNP_WLED_IBB_BIAS_MASK		0x7F
@@ -174,17 +181,10 @@ enum qpnp_wled_dim_mode {
	QPNP_WLED_DIM_HYBRID,
};

/* dimming curve shapes */
enum qpnp_wled_dim_shape {
	QPNP_WLED_DIM_SHAPE_LOG,
	QPNP_WLED_DIM_SHAPE_LINEAR,
	QPNP_WLED_DIM_SHAPE_SQUARE,
};

/* wled ctrl debug registers */
static u8 qpnp_wled_ctrl_dbg_regs[] = {
	0x44, 0x46, 0x48, 0x49, 0x4b, 0x4c, 0x4d, 0x4e, 0x50, 0x51, 0x52, 0x53,
	0x54, 0x55, 0x56, 0x57, 0x58, 0x5a, 0x5b, 0x5d, 0x5e
	0x54, 0x55, 0x56, 0x57, 0x58, 0x5a, 0x5b, 0x5d, 0x5e, 0xe2
};

/* wled sink debug registers */
@@ -214,7 +214,9 @@ static u8 qpnp_wled_lab_dbg_regs[] = {
 *  @ lock - mutex lock for exclusive access
 *  @ fdbk_op - output feedback mode
 *  @ dim_mode - dimming mode
 *  @ dim_shape - dimming curve shape
 *  @ ovp_irq - over voltage protection irq
 *  @ sc_irq - short circuit irq
 *  @ sc_cnt - short circuit irq count
 *  @ ctrl_base - base address for wled ctrl
 *  @ sink_base - base address for wled sink
 *  @ ibb_base - base address for IBB(Inverting Buck Boost)
@@ -238,6 +240,7 @@ static u8 qpnp_wled_lab_dbg_regs[] = {
 *  @ disp_type_amoled - type of display: LCD/AMOLED
 *  @ ibb_bias_active - activate display bias
 *  @ lab_fast_precharge - fast/slow precharge
 *  @ en_ext_pfet_sc_pro - enable sc protection on external pfet
 */
struct qpnp_wled {
	struct led_classdev	cdev;
@@ -246,7 +249,6 @@ struct qpnp_wled {
	struct mutex lock;
	enum qpnp_wled_fdbk_op fdbk_op;
	enum qpnp_wled_dim_mode dim_mode;
	enum qpnp_wled_dim_shape dim_shape;
	int ovp_irq;
	int sc_irq;
	u32 sc_cnt;
@@ -274,6 +276,7 @@ struct qpnp_wled {
	bool disp_type_amoled;
	bool ibb_bias_active;
	bool lab_fast_precharge;
	bool en_ext_pfet_sc_pro;
};

static struct qpnp_wled *gwled;
@@ -632,43 +635,6 @@ static ssize_t qpnp_wled_dim_mode_store(struct device *dev,
	return count;
}

/* sysfs show function for dimming curve shape*/
static ssize_t qpnp_wled_dim_shape_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct qpnp_wled *wled = dev_get_drvdata(dev);
	char *str;

	if (wled->dim_shape == QPNP_WLED_DIM_SHAPE_SQUARE)
		str = "square";
	else if (wled->dim_shape == QPNP_WLED_DIM_SHAPE_LOG)
		str = "log";
	else
		str = "linear";

	return snprintf(buf, PAGE_SIZE, "%s\n", str);
}

/* sysfs store function for dimming curve shape*/
static ssize_t qpnp_wled_dim_shape_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	struct qpnp_wled *wled = dev_get_drvdata(dev);
	char str[QPNP_WLED_STR_SIZE + 1];

	if (snprintf(str, QPNP_WLED_STR_SIZE, "%s", buf) > QPNP_WLED_STR_SIZE)
		return -EINVAL;

	if (strcmp(str, "log") == 0)
		wled->dim_shape = QPNP_WLED_DIM_SHAPE_LOG;
	else if (strcmp(str, "square") == 0)
		wled->dim_shape = QPNP_WLED_DIM_SHAPE_SQUARE;
	else
		wled->dim_shape = QPNP_WLED_DIM_SHAPE_LINEAR;

	return count;
}

/* sysfs show function for full scale current in ua*/
static ssize_t qpnp_wled_fs_curr_ua_show(struct device *dev,
		struct device_attribute *attr, char *buf)
@@ -761,9 +727,6 @@ static struct device_attribute qpnp_wled_attrs[] = {
	__ATTR(dim_mode, (S_IRUGO | S_IWUSR | S_IWGRP),
			qpnp_wled_dim_mode_show,
			qpnp_wled_dim_mode_store),
	__ATTR(dim_shape, (S_IRUGO | S_IWUSR | S_IWGRP),
			qpnp_wled_dim_shape_show,
			qpnp_wled_dim_shape_store),
	__ATTR(fs_curr_ua, (S_IRUGO | S_IWUSR | S_IWGRP),
			qpnp_wled_fs_curr_ua_show,
			qpnp_wled_fs_curr_ua_store),
@@ -1029,10 +992,14 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
	} else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_9600_KHZ) {
		wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
		temp = 1;
	} else {
	} else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_19200_KHZ) {
		wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_19200_KHZ;
		temp = 0;
	} else {
		wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
		temp = 1;
	}

	rc = qpnp_wled_read_reg(wled, &reg,
			QPNP_WLED_MOD_REG(wled->sink_base));
	if (rc < 0)
@@ -1043,6 +1010,9 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
	reg &= QPNP_WLED_PHASE_STAG_MASK;
	reg |= (wled->en_phase_stag << QPNP_WLED_PHASE_STAG_SHIFT);

	reg &= QPNP_WLED_ACC_CLK_FREQ_MASK;
	reg |= (temp << QPNP_WLED_ACC_CLK_FREQ_SHIFT);

	reg &= QPNP_WLED_DIM_RES_MASK;
	reg |= (wled->en_9b_dim_res << QPNP_WLED_DIM_RES_SHIFT);

@@ -1250,6 +1220,29 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
				wled->sc_irq, rc);
			return rc;
		}

		rc = qpnp_wled_read_reg(wled, &reg,
				QPNP_WLED_SC_PRO_REG(wled->ctrl_base));
		if (rc < 0)
			return rc;
		reg &= QPNP_WLED_EN_SC_MASK;
		reg |= 1 << QPNP_WLED_EN_SC_SHIFT;
		rc = qpnp_wled_write_reg(wled, &reg,
				QPNP_WLED_SC_PRO_REG(wled->ctrl_base));
		if (rc)
			return rc;

		if (wled->en_ext_pfet_sc_pro) {
			rc = qpnp_wled_sec_access(wled, wled->ctrl_base);
			if (rc)
				return rc;

			reg = QPNP_WLED_EXT_FET_DTEST2;
			rc = qpnp_wled_write_reg(wled, &reg,
					QPNP_WLED_TEST_REG(wled->ctrl_base));
			if (rc)
				return rc;
		}
	}

	return 0;
@@ -1352,7 +1345,7 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
		return rc;
	}

	wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_19200_KHZ;
	wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
	rc = of_property_read_u32(spmi->dev.of_node,
			"qcom,mod-freq-khz", &temp_val);
	if (!rc) {
@@ -1377,21 +1370,6 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
		return rc;
	}

	wled->dim_shape = QPNP_WLED_DIM_SHAPE_LINEAR;
	rc = of_property_read_string(spmi->dev.of_node,
			"qcom,dim-method", &temp_str);
	if (!rc) {
		if (strcmp(temp_str, "log") == 0)
			wled->dim_shape = QPNP_WLED_DIM_SHAPE_LOG;
		else if (strcmp(temp_str, "square") == 0)
			wled->dim_shape = QPNP_WLED_DIM_SHAPE_SQUARE;
		else
			wled->dim_shape = QPNP_WLED_DIM_SHAPE_LINEAR;
	} else if (rc != -EINVAL) {
		dev_err(&spmi->dev, "Unable to read dim method\n");
		return rc;
	}

	if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
		wled->hyb_thres = QPNP_WLED_DEF_HYB_THRES;
		rc = of_property_read_u32(spmi->dev.of_node,
@@ -1467,6 +1445,9 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
	if (wled->sc_irq < 0)
		dev_dbg(&spmi->dev, "sc irq is not used\n");

	wled->en_ext_pfet_sc_pro = of_property_read_bool(spmi->dev.of_node,
					"qcom,en-ext-pfet-sc-pro");

	return 0;
}