xref: /linux/drivers/video/backlight/qcom-wled.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
293c64f1eSCourtney Cavin /* Copyright (c) 2015, Sony Mobile Communications, AB.
393c64f1eSCourtney Cavin  */
493c64f1eSCourtney Cavin 
5feeab87bSKiran Gunda #include <linux/delay.h>
6feeab87bSKiran Gunda #include <linux/interrupt.h>
7feeab87bSKiran Gunda #include <linux/ktime.h>
893c64f1eSCourtney Cavin #include <linux/kernel.h>
97ddbc242SBjorn Andersson #include <linux/backlight.h>
1093c64f1eSCourtney Cavin #include <linux/module.h>
1193c64f1eSCourtney Cavin #include <linux/of.h>
1293c64f1eSCourtney Cavin #include <linux/of_device.h>
13775d2ffbSKiran Gunda #include <linux/of_address.h>
1493c64f1eSCourtney Cavin #include <linux/regmap.h>
1593c64f1eSCourtney Cavin 
169d6c2435SBjorn Andersson /* From DT binding */
17775d2ffbSKiran Gunda #define WLED_MAX_STRINGS				4
1862a1d3f6SSubbaraman Narayanamurthy #define MOD_A						0
1962a1d3f6SSubbaraman Narayanamurthy #define MOD_B						1
20775d2ffbSKiran Gunda 
21bb800a37SKiran Gunda #define WLED_DEFAULT_BRIGHTNESS				2048
228663c188SKiran Gunda #define WLED_SOFT_START_DLY_US				10000
23bb800a37SKiran Gunda #define WLED3_SINK_REG_BRIGHT_MAX			0xFFF
2462a1d3f6SSubbaraman Narayanamurthy #define WLED5_SINK_REG_BRIGHT_MAX_12B			0xFFF
2562a1d3f6SSubbaraman Narayanamurthy #define WLED5_SINK_REG_BRIGHT_MAX_15B			0x7FFF
2693c64f1eSCourtney Cavin 
2703b2b5e8SKiran Gunda /* WLED3/WLED4 control registers */
288663c188SKiran Gunda #define WLED3_CTRL_REG_FAULT_STATUS			0x08
298663c188SKiran Gunda #define  WLED3_CTRL_REG_ILIM_FAULT_BIT			BIT(0)
308663c188SKiran Gunda #define  WLED3_CTRL_REG_OVP_FAULT_BIT			BIT(1)
318663c188SKiran Gunda #define  WLED4_CTRL_REG_SC_FAULT_BIT			BIT(2)
3262a1d3f6SSubbaraman Narayanamurthy #define  WLED5_CTRL_REG_OVP_PRE_ALARM_BIT		BIT(4)
338663c188SKiran Gunda 
348663c188SKiran Gunda #define WLED3_CTRL_REG_INT_RT_STS			0x10
358663c188SKiran Gunda #define  WLED3_CTRL_REG_OVP_FAULT_STATUS		BIT(1)
368663c188SKiran Gunda 
37bb800a37SKiran Gunda #define WLED3_CTRL_REG_MOD_EN				0x46
38bb800a37SKiran Gunda #define  WLED3_CTRL_REG_MOD_EN_MASK			BIT(7)
39775d2ffbSKiran Gunda #define  WLED3_CTRL_REG_MOD_EN_SHIFT			7
4093c64f1eSCourtney Cavin 
418663c188SKiran Gunda #define WLED3_CTRL_REG_FEEDBACK_CONTROL			0x48
428663c188SKiran Gunda 
43bb800a37SKiran Gunda #define WLED3_CTRL_REG_FREQ				0x4c
44775d2ffbSKiran Gunda #define  WLED3_CTRL_REG_FREQ_MASK			GENMASK(3, 0)
4593c64f1eSCourtney Cavin 
46bb800a37SKiran Gunda #define WLED3_CTRL_REG_OVP				0x4d
47775d2ffbSKiran Gunda #define  WLED3_CTRL_REG_OVP_MASK			GENMASK(1, 0)
4862a1d3f6SSubbaraman Narayanamurthy #define  WLED5_CTRL_REG_OVP_MASK			GENMASK(3, 0)
4993c64f1eSCourtney Cavin 
50bb800a37SKiran Gunda #define WLED3_CTRL_REG_ILIMIT				0x4e
51775d2ffbSKiran Gunda #define  WLED3_CTRL_REG_ILIMIT_MASK			GENMASK(2, 0)
5293c64f1eSCourtney Cavin 
5303b2b5e8SKiran Gunda /* WLED3/WLED4 sink registers */
54bb800a37SKiran Gunda #define WLED3_SINK_REG_SYNC				0x47
55bb800a37SKiran Gunda #define  WLED3_SINK_REG_SYNC_CLEAR			0x00
5693c64f1eSCourtney Cavin 
57bb800a37SKiran Gunda #define WLED3_SINK_REG_CURR_SINK			0x4f
58775d2ffbSKiran Gunda #define  WLED3_SINK_REG_CURR_SINK_MASK			GENMASK(7, 5)
59775d2ffbSKiran Gunda #define  WLED3_SINK_REG_CURR_SINK_SHFT			5
6093c64f1eSCourtney Cavin 
61775d2ffbSKiran Gunda /* WLED3 specific per-'string' registers below */
62775d2ffbSKiran Gunda #define WLED3_SINK_REG_BRIGHT(n)			(0x40 + n)
6393c64f1eSCourtney Cavin 
64775d2ffbSKiran Gunda #define WLED3_SINK_REG_STR_MOD_EN(n)			(0x60 + (n * 0x10))
65bb800a37SKiran Gunda #define  WLED3_SINK_REG_STR_MOD_MASK			BIT(7)
6693c64f1eSCourtney Cavin 
67775d2ffbSKiran Gunda #define WLED3_SINK_REG_STR_FULL_SCALE_CURR(n)		(0x62 + (n * 0x10))
68775d2ffbSKiran Gunda #define  WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK	GENMASK(4, 0)
6993c64f1eSCourtney Cavin 
70775d2ffbSKiran Gunda #define WLED3_SINK_REG_STR_MOD_SRC(n)			(0x63 + (n * 0x10))
71775d2ffbSKiran Gunda #define  WLED3_SINK_REG_STR_MOD_SRC_MASK		BIT(0)
72bb800a37SKiran Gunda #define  WLED3_SINK_REG_STR_MOD_SRC_INT			0x00
73bb800a37SKiran Gunda #define  WLED3_SINK_REG_STR_MOD_SRC_EXT			0x01
7493c64f1eSCourtney Cavin 
75775d2ffbSKiran Gunda #define WLED3_SINK_REG_STR_CABC(n)			(0x66 + (n * 0x10))
76bb800a37SKiran Gunda #define  WLED3_SINK_REG_STR_CABC_MASK			BIT(7)
77775d2ffbSKiran Gunda 
78feeab87bSKiran Gunda /* WLED4 specific control registers */
79feeab87bSKiran Gunda #define WLED4_CTRL_REG_SHORT_PROTECT			0x5e
80feeab87bSKiran Gunda #define  WLED4_CTRL_REG_SHORT_EN_MASK			BIT(7)
81feeab87bSKiran Gunda 
82feeab87bSKiran Gunda #define WLED4_CTRL_REG_SEC_ACCESS			0xd0
83feeab87bSKiran Gunda #define  WLED4_CTRL_REG_SEC_UNLOCK			0xa5
84feeab87bSKiran Gunda 
85feeab87bSKiran Gunda #define WLED4_CTRL_REG_TEST1				0xe2
86feeab87bSKiran Gunda #define  WLED4_CTRL_REG_TEST1_EXT_FET_DTEST2		0x09
87feeab87bSKiran Gunda 
8803b2b5e8SKiran Gunda /* WLED4 specific sink registers */
8903b2b5e8SKiran Gunda #define WLED4_SINK_REG_CURR_SINK			0x46
9003b2b5e8SKiran Gunda #define  WLED4_SINK_REG_CURR_SINK_MASK			GENMASK(7, 4)
9103b2b5e8SKiran Gunda #define  WLED4_SINK_REG_CURR_SINK_SHFT			4
9203b2b5e8SKiran Gunda 
9303b2b5e8SKiran Gunda /* WLED4 specific per-'string' registers below */
9403b2b5e8SKiran Gunda #define WLED4_SINK_REG_STR_MOD_EN(n)			(0x50 + (n * 0x10))
9503b2b5e8SKiran Gunda #define  WLED4_SINK_REG_STR_MOD_MASK			BIT(7)
9603b2b5e8SKiran Gunda 
9703b2b5e8SKiran Gunda #define WLED4_SINK_REG_STR_FULL_SCALE_CURR(n)		(0x52 + (n * 0x10))
9803b2b5e8SKiran Gunda #define  WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK	GENMASK(3, 0)
9903b2b5e8SKiran Gunda 
10003b2b5e8SKiran Gunda #define WLED4_SINK_REG_STR_MOD_SRC(n)			(0x53 + (n * 0x10))
10103b2b5e8SKiran Gunda #define  WLED4_SINK_REG_STR_MOD_SRC_MASK		BIT(0)
10203b2b5e8SKiran Gunda #define  WLED4_SINK_REG_STR_MOD_SRC_INT			0x00
10303b2b5e8SKiran Gunda #define  WLED4_SINK_REG_STR_MOD_SRC_EXT			0x01
10403b2b5e8SKiran Gunda 
10503b2b5e8SKiran Gunda #define WLED4_SINK_REG_STR_CABC(n)			(0x56 + (n * 0x10))
10603b2b5e8SKiran Gunda #define  WLED4_SINK_REG_STR_CABC_MASK			BIT(7)
10703b2b5e8SKiran Gunda 
10803b2b5e8SKiran Gunda #define WLED4_SINK_REG_BRIGHT(n)			(0x57 + (n * 0x10))
10903b2b5e8SKiran Gunda 
11062a1d3f6SSubbaraman Narayanamurthy /* WLED5 specific control registers */
11162a1d3f6SSubbaraman Narayanamurthy #define WLED5_CTRL_REG_OVP_INT_CTL			0x5f
11262a1d3f6SSubbaraman Narayanamurthy #define  WLED5_CTRL_REG_OVP_INT_TIMER_MASK		GENMASK(2, 0)
11362a1d3f6SSubbaraman Narayanamurthy 
11462a1d3f6SSubbaraman Narayanamurthy /* WLED5 specific sink registers */
11562a1d3f6SSubbaraman Narayanamurthy #define WLED5_SINK_REG_MOD_A_EN				0x50
11662a1d3f6SSubbaraman Narayanamurthy #define WLED5_SINK_REG_MOD_B_EN				0x60
11762a1d3f6SSubbaraman Narayanamurthy #define  WLED5_SINK_REG_MOD_EN_MASK			BIT(7)
11862a1d3f6SSubbaraman Narayanamurthy 
11962a1d3f6SSubbaraman Narayanamurthy #define WLED5_SINK_REG_MOD_A_SRC_SEL			0x51
12062a1d3f6SSubbaraman Narayanamurthy #define WLED5_SINK_REG_MOD_B_SRC_SEL			0x61
12162a1d3f6SSubbaraman Narayanamurthy #define  WLED5_SINK_REG_MOD_SRC_SEL_HIGH		0
12262a1d3f6SSubbaraman Narayanamurthy #define  WLED5_SINK_REG_MOD_SRC_SEL_EXT			0x03
12362a1d3f6SSubbaraman Narayanamurthy #define  WLED5_SINK_REG_MOD_SRC_SEL_MASK		GENMASK(1, 0)
12462a1d3f6SSubbaraman Narayanamurthy 
12562a1d3f6SSubbaraman Narayanamurthy #define WLED5_SINK_REG_MOD_A_BRIGHTNESS_WIDTH_SEL	0x52
12662a1d3f6SSubbaraman Narayanamurthy #define WLED5_SINK_REG_MOD_B_BRIGHTNESS_WIDTH_SEL	0x62
12762a1d3f6SSubbaraman Narayanamurthy #define  WLED5_SINK_REG_BRIGHTNESS_WIDTH_12B		0
12862a1d3f6SSubbaraman Narayanamurthy #define  WLED5_SINK_REG_BRIGHTNESS_WIDTH_15B		1
12962a1d3f6SSubbaraman Narayanamurthy 
13062a1d3f6SSubbaraman Narayanamurthy #define WLED5_SINK_REG_MOD_A_BRIGHTNESS_LSB		0x53
13162a1d3f6SSubbaraman Narayanamurthy #define WLED5_SINK_REG_MOD_A_BRIGHTNESS_MSB		0x54
13262a1d3f6SSubbaraman Narayanamurthy #define WLED5_SINK_REG_MOD_B_BRIGHTNESS_LSB		0x63
13362a1d3f6SSubbaraman Narayanamurthy #define WLED5_SINK_REG_MOD_B_BRIGHTNESS_MSB		0x64
13462a1d3f6SSubbaraman Narayanamurthy 
13562a1d3f6SSubbaraman Narayanamurthy #define WLED5_SINK_REG_MOD_SYNC_BIT			0x65
13662a1d3f6SSubbaraman Narayanamurthy #define  WLED5_SINK_REG_SYNC_MOD_A_BIT			BIT(0)
13762a1d3f6SSubbaraman Narayanamurthy #define  WLED5_SINK_REG_SYNC_MOD_B_BIT			BIT(1)
13862a1d3f6SSubbaraman Narayanamurthy #define  WLED5_SINK_REG_SYNC_MASK			GENMASK(1, 0)
13962a1d3f6SSubbaraman Narayanamurthy 
14062a1d3f6SSubbaraman Narayanamurthy /* WLED5 specific per-'string' registers below */
14162a1d3f6SSubbaraman Narayanamurthy #define WLED5_SINK_REG_STR_FULL_SCALE_CURR(n)		(0x72 + (n * 0x10))
14262a1d3f6SSubbaraman Narayanamurthy 
14362a1d3f6SSubbaraman Narayanamurthy #define WLED5_SINK_REG_STR_SRC_SEL(n)			(0x73 + (n * 0x10))
14462a1d3f6SSubbaraman Narayanamurthy #define  WLED5_SINK_REG_SRC_SEL_MOD_A			0
14562a1d3f6SSubbaraman Narayanamurthy #define  WLED5_SINK_REG_SRC_SEL_MOD_B			1
14662a1d3f6SSubbaraman Narayanamurthy #define  WLED5_SINK_REG_SRC_SEL_MASK			GENMASK(1, 0)
14762a1d3f6SSubbaraman Narayanamurthy 
148775d2ffbSKiran Gunda struct wled_var_cfg {
149775d2ffbSKiran Gunda 	const u32 *values;
150775d2ffbSKiran Gunda 	u32 (*fn)(u32);
151775d2ffbSKiran Gunda 	int size;
152775d2ffbSKiran Gunda };
153775d2ffbSKiran Gunda 
154775d2ffbSKiran Gunda struct wled_u32_opts {
155775d2ffbSKiran Gunda 	const char *name;
156775d2ffbSKiran Gunda 	u32 *val_ptr;
157775d2ffbSKiran Gunda 	const struct wled_var_cfg *cfg;
158775d2ffbSKiran Gunda };
159775d2ffbSKiran Gunda 
160775d2ffbSKiran Gunda struct wled_bool_opts {
161775d2ffbSKiran Gunda 	const char *name;
162775d2ffbSKiran Gunda 	bool *val_ptr;
163775d2ffbSKiran Gunda };
16493c64f1eSCourtney Cavin 
165bb800a37SKiran Gunda struct wled_config {
166bb800a37SKiran Gunda 	u32 boost_i_limit;
16793c64f1eSCourtney Cavin 	u32 ovp;
16893c64f1eSCourtney Cavin 	u32 switch_freq;
16993c64f1eSCourtney Cavin 	u32 num_strings;
170bb800a37SKiran Gunda 	u32 string_i_limit;
171775d2ffbSKiran Gunda 	u32 enabled_strings[WLED_MAX_STRINGS];
17262a1d3f6SSubbaraman Narayanamurthy 	u32 mod_sel;
17362a1d3f6SSubbaraman Narayanamurthy 	u32 cabc_sel;
17493c64f1eSCourtney Cavin 	bool cs_out_en;
17593c64f1eSCourtney Cavin 	bool ext_gen;
176775d2ffbSKiran Gunda 	bool cabc;
177feeab87bSKiran Gunda 	bool external_pfet;
1788663c188SKiran Gunda 	bool auto_detection_enabled;
17993c64f1eSCourtney Cavin };
18093c64f1eSCourtney Cavin 
181bb800a37SKiran Gunda struct wled {
1827ddbc242SBjorn Andersson 	const char *name;
183775d2ffbSKiran Gunda 	struct device *dev;
18493c64f1eSCourtney Cavin 	struct regmap *regmap;
185feeab87bSKiran Gunda 	struct mutex lock;	/* Lock to avoid race from thread irq handler */
186feeab87bSKiran Gunda 	ktime_t last_short_event;
1878663c188SKiran Gunda 	ktime_t start_ovp_fault_time;
188775d2ffbSKiran Gunda 	u16 ctrl_addr;
18903b2b5e8SKiran Gunda 	u16 sink_addr;
190775d2ffbSKiran Gunda 	u16 max_string_count;
1918663c188SKiran Gunda 	u16 auto_detection_ovp_count;
192775d2ffbSKiran Gunda 	u32 brightness;
193775d2ffbSKiran Gunda 	u32 max_brightness;
194feeab87bSKiran Gunda 	u32 short_count;
1958663c188SKiran Gunda 	u32 auto_detect_count;
196f16899a6SKiran Gunda 	u32 version;
197feeab87bSKiran Gunda 	bool disabled_by_short;
198feeab87bSKiran Gunda 	bool has_short_detect;
19962a1d3f6SSubbaraman Narayanamurthy 	bool cabc_disabled;
200feeab87bSKiran Gunda 	int short_irq;
2018663c188SKiran Gunda 	int ovp_irq;
20293c64f1eSCourtney Cavin 
203bb800a37SKiran Gunda 	struct wled_config cfg;
2048663c188SKiran Gunda 	struct delayed_work ovp_work;
205f16899a6SKiran Gunda 
206f16899a6SKiran Gunda 	/* Configures the brightness. Applicable for wled3, wled4 and wled5 */
207775d2ffbSKiran Gunda 	int (*wled_set_brightness)(struct wled *wled, u16 brightness);
208f16899a6SKiran Gunda 
209f16899a6SKiran Gunda 	/* Configures the cabc register. Applicable for wled4 and wled5 */
210f16899a6SKiran Gunda 	int (*wled_cabc_config)(struct wled *wled, bool enable);
211f16899a6SKiran Gunda 
212f16899a6SKiran Gunda 	/*
213f16899a6SKiran Gunda 	 * Toggles the sync bit for the brightness update to take place.
214f16899a6SKiran Gunda 	 * Applicable for WLED3, WLED4 and WLED5.
215f16899a6SKiran Gunda 	 */
216f16899a6SKiran Gunda 	int (*wled_sync_toggle)(struct wled *wled);
217f16899a6SKiran Gunda 
218f16899a6SKiran Gunda 	/*
219f16899a6SKiran Gunda 	 * Time to wait before checking the OVP status after wled module enable.
220f16899a6SKiran Gunda 	 * Applicable for WLED4 and WLED5.
221f16899a6SKiran Gunda 	 */
222f16899a6SKiran Gunda 	int (*wled_ovp_delay)(struct wled *wled);
223f16899a6SKiran Gunda 
224f16899a6SKiran Gunda 	/*
225f16899a6SKiran Gunda 	 * Determines if the auto string detection is required.
226f16899a6SKiran Gunda 	 * Applicable for WLED4 and WLED5
227f16899a6SKiran Gunda 	 */
228f16899a6SKiran Gunda 	bool (*wled_auto_detection_required)(struct wled *wled);
22993c64f1eSCourtney Cavin };
23093c64f1eSCourtney Cavin 
231775d2ffbSKiran Gunda static int wled3_set_brightness(struct wled *wled, u16 brightness)
232775d2ffbSKiran Gunda {
233775d2ffbSKiran Gunda 	int rc, i;
2340a139358SMarijn Suijten 	__le16 v;
235775d2ffbSKiran Gunda 
2360a139358SMarijn Suijten 	v = cpu_to_le16(brightness & WLED3_SINK_REG_BRIGHT_MAX);
237775d2ffbSKiran Gunda 
238775d2ffbSKiran Gunda 	for (i = 0; i < wled->cfg.num_strings; ++i) {
239775d2ffbSKiran Gunda 		rc = regmap_bulk_write(wled->regmap, wled->ctrl_addr +
240ec961cf3SMarijn Suijten 				       WLED3_SINK_REG_BRIGHT(wled->cfg.enabled_strings[i]),
2410a139358SMarijn Suijten 				       &v, sizeof(v));
242775d2ffbSKiran Gunda 		if (rc < 0)
243775d2ffbSKiran Gunda 			return rc;
244775d2ffbSKiran Gunda 	}
245775d2ffbSKiran Gunda 
246775d2ffbSKiran Gunda 	return 0;
247775d2ffbSKiran Gunda }
248775d2ffbSKiran Gunda 
24903b2b5e8SKiran Gunda static int wled4_set_brightness(struct wled *wled, u16 brightness)
25003b2b5e8SKiran Gunda {
25103b2b5e8SKiran Gunda 	int rc, i;
25203b2b5e8SKiran Gunda 	u16 low_limit = wled->max_brightness * 4 / 1000;
2530a139358SMarijn Suijten 	__le16 v;
25403b2b5e8SKiran Gunda 
25503b2b5e8SKiran Gunda 	/* WLED4's lower limit of operation is 0.4% */
25603b2b5e8SKiran Gunda 	if (brightness > 0 && brightness < low_limit)
25703b2b5e8SKiran Gunda 		brightness = low_limit;
25803b2b5e8SKiran Gunda 
2590a139358SMarijn Suijten 	v = cpu_to_le16(brightness & WLED3_SINK_REG_BRIGHT_MAX);
26003b2b5e8SKiran Gunda 
26103b2b5e8SKiran Gunda 	for (i = 0; i < wled->cfg.num_strings; ++i) {
26203b2b5e8SKiran Gunda 		rc = regmap_bulk_write(wled->regmap, wled->sink_addr +
263ec961cf3SMarijn Suijten 				       WLED4_SINK_REG_BRIGHT(wled->cfg.enabled_strings[i]),
2640a139358SMarijn Suijten 				       &v, sizeof(v));
26503b2b5e8SKiran Gunda 		if (rc < 0)
26603b2b5e8SKiran Gunda 			return rc;
26703b2b5e8SKiran Gunda 	}
26803b2b5e8SKiran Gunda 
26903b2b5e8SKiran Gunda 	return 0;
27003b2b5e8SKiran Gunda }
27103b2b5e8SKiran Gunda 
27262a1d3f6SSubbaraman Narayanamurthy static int wled5_set_brightness(struct wled *wled, u16 brightness)
27362a1d3f6SSubbaraman Narayanamurthy {
27462a1d3f6SSubbaraman Narayanamurthy 	int rc, offset;
27562a1d3f6SSubbaraman Narayanamurthy 	u16 low_limit = wled->max_brightness * 1 / 1000;
2760a139358SMarijn Suijten 	__le16 v;
27762a1d3f6SSubbaraman Narayanamurthy 
27862a1d3f6SSubbaraman Narayanamurthy 	/* WLED5's lower limit is 0.1% */
27962a1d3f6SSubbaraman Narayanamurthy 	if (brightness < low_limit)
28062a1d3f6SSubbaraman Narayanamurthy 		brightness = low_limit;
28162a1d3f6SSubbaraman Narayanamurthy 
2820a139358SMarijn Suijten 	v = cpu_to_le16(brightness & WLED5_SINK_REG_BRIGHT_MAX_15B);
28362a1d3f6SSubbaraman Narayanamurthy 
28462a1d3f6SSubbaraman Narayanamurthy 	offset = (wled->cfg.mod_sel == MOD_A) ?
28562a1d3f6SSubbaraman Narayanamurthy 		  WLED5_SINK_REG_MOD_A_BRIGHTNESS_LSB :
28662a1d3f6SSubbaraman Narayanamurthy 		  WLED5_SINK_REG_MOD_B_BRIGHTNESS_LSB;
28762a1d3f6SSubbaraman Narayanamurthy 
28862a1d3f6SSubbaraman Narayanamurthy 	rc = regmap_bulk_write(wled->regmap, wled->sink_addr + offset,
2890a139358SMarijn Suijten 			       &v, sizeof(v));
29062a1d3f6SSubbaraman Narayanamurthy 	return rc;
29162a1d3f6SSubbaraman Narayanamurthy }
29262a1d3f6SSubbaraman Narayanamurthy 
2938663c188SKiran Gunda static void wled_ovp_work(struct work_struct *work)
2948663c188SKiran Gunda {
2958663c188SKiran Gunda 	struct wled *wled = container_of(work,
2968663c188SKiran Gunda 					 struct wled, ovp_work.work);
2978663c188SKiran Gunda 	enable_irq(wled->ovp_irq);
2988663c188SKiran Gunda }
2998663c188SKiran Gunda 
300775d2ffbSKiran Gunda static int wled_module_enable(struct wled *wled, int val)
301775d2ffbSKiran Gunda {
302775d2ffbSKiran Gunda 	int rc;
303775d2ffbSKiran Gunda 
304feeab87bSKiran Gunda 	if (wled->disabled_by_short)
305feeab87bSKiran Gunda 		return -ENXIO;
306feeab87bSKiran Gunda 
307775d2ffbSKiran Gunda 	rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
308775d2ffbSKiran Gunda 				WLED3_CTRL_REG_MOD_EN,
309775d2ffbSKiran Gunda 				WLED3_CTRL_REG_MOD_EN_MASK,
310775d2ffbSKiran Gunda 				val << WLED3_CTRL_REG_MOD_EN_SHIFT);
3118663c188SKiran Gunda 	if (rc < 0)
312775d2ffbSKiran Gunda 		return rc;
3138663c188SKiran Gunda 
3148663c188SKiran Gunda 	if (wled->ovp_irq > 0) {
3158663c188SKiran Gunda 		if (val) {
3168663c188SKiran Gunda 			/*
3178663c188SKiran Gunda 			 * The hardware generates a storm of spurious OVP
3188663c188SKiran Gunda 			 * interrupts during soft start operations. So defer
3198663c188SKiran Gunda 			 * enabling the IRQ for 10ms to ensure that the
3208663c188SKiran Gunda 			 * soft start is complete.
3218663c188SKiran Gunda 			 */
3228663c188SKiran Gunda 			schedule_delayed_work(&wled->ovp_work, HZ / 100);
3238663c188SKiran Gunda 		} else {
3248663c188SKiran Gunda 			if (!cancel_delayed_work_sync(&wled->ovp_work))
3258663c188SKiran Gunda 				disable_irq(wled->ovp_irq);
3268663c188SKiran Gunda 		}
3278663c188SKiran Gunda 	}
3288663c188SKiran Gunda 
3298663c188SKiran Gunda 	return 0;
330775d2ffbSKiran Gunda }
331775d2ffbSKiran Gunda 
332f16899a6SKiran Gunda static int wled3_sync_toggle(struct wled *wled)
333775d2ffbSKiran Gunda {
334775d2ffbSKiran Gunda 	int rc;
335775d2ffbSKiran Gunda 	unsigned int mask = GENMASK(wled->max_string_count - 1, 0);
336775d2ffbSKiran Gunda 
337775d2ffbSKiran Gunda 	rc = regmap_update_bits(wled->regmap,
338cdfd4c68SObeida Shamoun 				wled->sink_addr + WLED3_SINK_REG_SYNC,
3395eb622eeSKiran Gunda 				mask, WLED3_SINK_REG_SYNC_CLEAR);
340775d2ffbSKiran Gunda 	if (rc < 0)
341775d2ffbSKiran Gunda 		return rc;
342775d2ffbSKiran Gunda 
343775d2ffbSKiran Gunda 	rc = regmap_update_bits(wled->regmap,
344cdfd4c68SObeida Shamoun 				wled->sink_addr + WLED3_SINK_REG_SYNC,
3455eb622eeSKiran Gunda 				mask, mask);
346775d2ffbSKiran Gunda 
347775d2ffbSKiran Gunda 	return rc;
348775d2ffbSKiran Gunda }
349775d2ffbSKiran Gunda 
3504d6e9cdfSKiran Gunda static int wled5_mod_sync_toggle(struct wled *wled)
35162a1d3f6SSubbaraman Narayanamurthy {
35262a1d3f6SSubbaraman Narayanamurthy 	int rc;
35362a1d3f6SSubbaraman Narayanamurthy 	u8 val;
35462a1d3f6SSubbaraman Narayanamurthy 
35562a1d3f6SSubbaraman Narayanamurthy 	rc = regmap_update_bits(wled->regmap,
35662a1d3f6SSubbaraman Narayanamurthy 				wled->sink_addr + WLED5_SINK_REG_MOD_SYNC_BIT,
3575eb622eeSKiran Gunda 				WLED5_SINK_REG_SYNC_MASK, 0);
35862a1d3f6SSubbaraman Narayanamurthy 	if (rc < 0)
35962a1d3f6SSubbaraman Narayanamurthy 		return rc;
36062a1d3f6SSubbaraman Narayanamurthy 
3615eb622eeSKiran Gunda 	val = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_REG_SYNC_MOD_A_BIT :
3625eb622eeSKiran Gunda 					     WLED5_SINK_REG_SYNC_MOD_B_BIT;
36362a1d3f6SSubbaraman Narayanamurthy 	return regmap_update_bits(wled->regmap,
36462a1d3f6SSubbaraman Narayanamurthy 				  wled->sink_addr + WLED5_SINK_REG_MOD_SYNC_BIT,
3655eb622eeSKiran Gunda 				  WLED5_SINK_REG_SYNC_MASK, val);
36662a1d3f6SSubbaraman Narayanamurthy }
36762a1d3f6SSubbaraman Narayanamurthy 
368f16899a6SKiran Gunda static int wled_ovp_fault_status(struct wled *wled, bool *fault_set)
369f16899a6SKiran Gunda {
370f16899a6SKiran Gunda 	int rc;
371f16899a6SKiran Gunda 	u32 int_rt_sts, fault_sts;
372f16899a6SKiran Gunda 
373f16899a6SKiran Gunda 	*fault_set = false;
374f16899a6SKiran Gunda 	rc = regmap_read(wled->regmap,
375f16899a6SKiran Gunda 			wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS,
376f16899a6SKiran Gunda 			&int_rt_sts);
377f16899a6SKiran Gunda 	if (rc < 0) {
378f16899a6SKiran Gunda 		dev_err(wled->dev, "Failed to read INT_RT_STS rc=%d\n", rc);
379f16899a6SKiran Gunda 		return rc;
380f16899a6SKiran Gunda 	}
381f16899a6SKiran Gunda 
382f16899a6SKiran Gunda 	rc = regmap_read(wled->regmap,
383f16899a6SKiran Gunda 			wled->ctrl_addr + WLED3_CTRL_REG_FAULT_STATUS,
384f16899a6SKiran Gunda 			&fault_sts);
385f16899a6SKiran Gunda 	if (rc < 0) {
386f16899a6SKiran Gunda 		dev_err(wled->dev, "Failed to read FAULT_STATUS rc=%d\n", rc);
387f16899a6SKiran Gunda 		return rc;
388f16899a6SKiran Gunda 	}
389f16899a6SKiran Gunda 
390f16899a6SKiran Gunda 	if (int_rt_sts & WLED3_CTRL_REG_OVP_FAULT_STATUS)
391f16899a6SKiran Gunda 		*fault_set = true;
392f16899a6SKiran Gunda 
39362a1d3f6SSubbaraman Narayanamurthy 	if (wled->version == 4 && (fault_sts & WLED3_CTRL_REG_OVP_FAULT_BIT))
39462a1d3f6SSubbaraman Narayanamurthy 		*fault_set = true;
39562a1d3f6SSubbaraman Narayanamurthy 
39662a1d3f6SSubbaraman Narayanamurthy 	if (wled->version == 5 && (fault_sts & (WLED3_CTRL_REG_OVP_FAULT_BIT |
39762a1d3f6SSubbaraman Narayanamurthy 				   WLED5_CTRL_REG_OVP_PRE_ALARM_BIT)))
398f16899a6SKiran Gunda 		*fault_set = true;
399f16899a6SKiran Gunda 
400f16899a6SKiran Gunda 	if (*fault_set)
401f16899a6SKiran Gunda 		dev_dbg(wled->dev, "WLED OVP fault detected, int_rt_sts=0x%x fault_sts=0x%x\n",
402f16899a6SKiran Gunda 			int_rt_sts, fault_sts);
403f16899a6SKiran Gunda 
404f16899a6SKiran Gunda 	return rc;
405f16899a6SKiran Gunda }
406f16899a6SKiran Gunda 
407f16899a6SKiran Gunda static int wled4_ovp_delay(struct wled *wled)
408f16899a6SKiran Gunda {
409f16899a6SKiran Gunda 	return WLED_SOFT_START_DLY_US;
410f16899a6SKiran Gunda }
411f16899a6SKiran Gunda 
41262a1d3f6SSubbaraman Narayanamurthy static int wled5_ovp_delay(struct wled *wled)
41362a1d3f6SSubbaraman Narayanamurthy {
41462a1d3f6SSubbaraman Narayanamurthy 	int rc, delay_us;
41562a1d3f6SSubbaraman Narayanamurthy 	u32 val;
41662a1d3f6SSubbaraman Narayanamurthy 	u8 ovp_timer_ms[8] = {1, 2, 4, 8, 12, 16, 20, 24};
41762a1d3f6SSubbaraman Narayanamurthy 
41862a1d3f6SSubbaraman Narayanamurthy 	/* For WLED5, get the delay based on OVP timer */
41962a1d3f6SSubbaraman Narayanamurthy 	rc = regmap_read(wled->regmap, wled->ctrl_addr +
42062a1d3f6SSubbaraman Narayanamurthy 			 WLED5_CTRL_REG_OVP_INT_CTL, &val);
42162a1d3f6SSubbaraman Narayanamurthy 	if (rc < 0)
42262a1d3f6SSubbaraman Narayanamurthy 		delay_us =
42362a1d3f6SSubbaraman Narayanamurthy 		ovp_timer_ms[val & WLED5_CTRL_REG_OVP_INT_TIMER_MASK] * 1000;
42462a1d3f6SSubbaraman Narayanamurthy 	else
42562a1d3f6SSubbaraman Narayanamurthy 		delay_us = 2 * WLED_SOFT_START_DLY_US;
42662a1d3f6SSubbaraman Narayanamurthy 
42762a1d3f6SSubbaraman Narayanamurthy 	dev_dbg(wled->dev, "delay_time_us: %d\n", delay_us);
42862a1d3f6SSubbaraman Narayanamurthy 
42962a1d3f6SSubbaraman Narayanamurthy 	return delay_us;
43062a1d3f6SSubbaraman Narayanamurthy }
43162a1d3f6SSubbaraman Narayanamurthy 
432bb800a37SKiran Gunda static int wled_update_status(struct backlight_device *bl)
43393c64f1eSCourtney Cavin {
434bb800a37SKiran Gunda 	struct wled *wled = bl_get_data(bl);
43551d53e5bSSam Ravnborg 	u16 brightness = backlight_get_brightness(bl);
436775d2ffbSKiran Gunda 	int rc = 0;
43793c64f1eSCourtney Cavin 
438feeab87bSKiran Gunda 	mutex_lock(&wled->lock);
439775d2ffbSKiran Gunda 	if (brightness) {
440775d2ffbSKiran Gunda 		rc = wled->wled_set_brightness(wled, brightness);
441775d2ffbSKiran Gunda 		if (rc < 0) {
442775d2ffbSKiran Gunda 			dev_err(wled->dev, "wled failed to set brightness rc:%d\n",
443775d2ffbSKiran Gunda 				rc);
444feeab87bSKiran Gunda 			goto unlock_mutex;
44593c64f1eSCourtney Cavin 		}
44693c64f1eSCourtney Cavin 
4474d6e9cdfSKiran Gunda 		if (wled->version < 5) {
448f16899a6SKiran Gunda 			rc = wled->wled_sync_toggle(wled);
449775d2ffbSKiran Gunda 			if (rc < 0) {
450775d2ffbSKiran Gunda 				dev_err(wled->dev, "wled sync failed rc:%d\n", rc);
451feeab87bSKiran Gunda 				goto unlock_mutex;
452775d2ffbSKiran Gunda 			}
4534d6e9cdfSKiran Gunda 		} else {
4544d6e9cdfSKiran Gunda 			/*
4554d6e9cdfSKiran Gunda 			 * For WLED5 toggling the MOD_SYNC_BIT updates the
4564d6e9cdfSKiran Gunda 			 * brightness
4574d6e9cdfSKiran Gunda 			 */
4584d6e9cdfSKiran Gunda 			rc = wled5_mod_sync_toggle(wled);
4594d6e9cdfSKiran Gunda 			if (rc < 0) {
4604d6e9cdfSKiran Gunda 				dev_err(wled->dev, "wled mod sync failed rc:%d\n",
4614d6e9cdfSKiran Gunda 					rc);
4624d6e9cdfSKiran Gunda 				goto unlock_mutex;
4634d6e9cdfSKiran Gunda 			}
4644d6e9cdfSKiran Gunda 		}
465775d2ffbSKiran Gunda 	}
46693c64f1eSCourtney Cavin 
467775d2ffbSKiran Gunda 	if (!!brightness != !!wled->brightness) {
468775d2ffbSKiran Gunda 		rc = wled_module_enable(wled, !!brightness);
469775d2ffbSKiran Gunda 		if (rc < 0) {
470775d2ffbSKiran Gunda 			dev_err(wled->dev, "wled enable failed rc:%d\n", rc);
471feeab87bSKiran Gunda 			goto unlock_mutex;
472775d2ffbSKiran Gunda 		}
473775d2ffbSKiran Gunda 	}
474775d2ffbSKiran Gunda 
475775d2ffbSKiran Gunda 	wled->brightness = brightness;
476775d2ffbSKiran Gunda 
477feeab87bSKiran Gunda unlock_mutex:
478feeab87bSKiran Gunda 	mutex_unlock(&wled->lock);
479feeab87bSKiran Gunda 
48093c64f1eSCourtney Cavin 	return rc;
48193c64f1eSCourtney Cavin }
48293c64f1eSCourtney Cavin 
483f16899a6SKiran Gunda static int wled4_cabc_config(struct wled *wled, bool enable)
484f16899a6SKiran Gunda {
485f16899a6SKiran Gunda 	int i, j, rc;
486f16899a6SKiran Gunda 	u8 val;
487f16899a6SKiran Gunda 
488f16899a6SKiran Gunda 	for (i = 0; i < wled->cfg.num_strings; i++) {
489f16899a6SKiran Gunda 		j = wled->cfg.enabled_strings[i];
490f16899a6SKiran Gunda 
491f16899a6SKiran Gunda 		val = enable ? WLED4_SINK_REG_STR_CABC_MASK : 0;
492f16899a6SKiran Gunda 		rc = regmap_update_bits(wled->regmap, wled->sink_addr +
493f16899a6SKiran Gunda 					WLED4_SINK_REG_STR_CABC(j),
494f16899a6SKiran Gunda 					WLED4_SINK_REG_STR_CABC_MASK, val);
495f16899a6SKiran Gunda 		if (rc < 0)
496f16899a6SKiran Gunda 			return rc;
497f16899a6SKiran Gunda 	}
498f16899a6SKiran Gunda 
499f16899a6SKiran Gunda 	return 0;
500f16899a6SKiran Gunda }
501f16899a6SKiran Gunda 
50262a1d3f6SSubbaraman Narayanamurthy static int wled5_cabc_config(struct wled *wled, bool enable)
50362a1d3f6SSubbaraman Narayanamurthy {
50462a1d3f6SSubbaraman Narayanamurthy 	int rc, offset;
50562a1d3f6SSubbaraman Narayanamurthy 	u8 reg;
50662a1d3f6SSubbaraman Narayanamurthy 
50762a1d3f6SSubbaraman Narayanamurthy 	if (wled->cabc_disabled)
50862a1d3f6SSubbaraman Narayanamurthy 		return 0;
50962a1d3f6SSubbaraman Narayanamurthy 
51062a1d3f6SSubbaraman Narayanamurthy 	reg = enable ? wled->cfg.cabc_sel : 0;
51162a1d3f6SSubbaraman Narayanamurthy 	offset = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_REG_MOD_A_SRC_SEL :
51262a1d3f6SSubbaraman Narayanamurthy 						WLED5_SINK_REG_MOD_B_SRC_SEL;
51362a1d3f6SSubbaraman Narayanamurthy 
51462a1d3f6SSubbaraman Narayanamurthy 	rc = regmap_update_bits(wled->regmap, wled->sink_addr + offset,
51562a1d3f6SSubbaraman Narayanamurthy 				WLED5_SINK_REG_MOD_SRC_SEL_MASK, reg);
51662a1d3f6SSubbaraman Narayanamurthy 	if (rc < 0) {
51762a1d3f6SSubbaraman Narayanamurthy 		pr_err("Error in configuring CABC rc=%d\n", rc);
51862a1d3f6SSubbaraman Narayanamurthy 		return rc;
51962a1d3f6SSubbaraman Narayanamurthy 	}
52062a1d3f6SSubbaraman Narayanamurthy 
52162a1d3f6SSubbaraman Narayanamurthy 	if (!wled->cfg.cabc_sel)
52262a1d3f6SSubbaraman Narayanamurthy 		wled->cabc_disabled = true;
52362a1d3f6SSubbaraman Narayanamurthy 
52462a1d3f6SSubbaraman Narayanamurthy 	return 0;
52562a1d3f6SSubbaraman Narayanamurthy }
52662a1d3f6SSubbaraman Narayanamurthy 
527feeab87bSKiran Gunda #define WLED_SHORT_DLY_MS			20
528feeab87bSKiran Gunda #define WLED_SHORT_CNT_MAX			5
529feeab87bSKiran Gunda #define WLED_SHORT_RESET_CNT_DLY_US		USEC_PER_SEC
530feeab87bSKiran Gunda 
531feeab87bSKiran Gunda static irqreturn_t wled_short_irq_handler(int irq, void *_wled)
532feeab87bSKiran Gunda {
533feeab87bSKiran Gunda 	struct wled *wled = _wled;
534feeab87bSKiran Gunda 	int rc;
535feeab87bSKiran Gunda 	s64 elapsed_time;
536feeab87bSKiran Gunda 
537feeab87bSKiran Gunda 	wled->short_count++;
538feeab87bSKiran Gunda 	mutex_lock(&wled->lock);
539feeab87bSKiran Gunda 	rc = wled_module_enable(wled, false);
540feeab87bSKiran Gunda 	if (rc < 0) {
541feeab87bSKiran Gunda 		dev_err(wled->dev, "wled disable failed rc:%d\n", rc);
542feeab87bSKiran Gunda 		goto unlock_mutex;
543feeab87bSKiran Gunda 	}
544feeab87bSKiran Gunda 
545feeab87bSKiran Gunda 	elapsed_time = ktime_us_delta(ktime_get(),
546feeab87bSKiran Gunda 				      wled->last_short_event);
547feeab87bSKiran Gunda 	if (elapsed_time > WLED_SHORT_RESET_CNT_DLY_US)
548feeab87bSKiran Gunda 		wled->short_count = 1;
549feeab87bSKiran Gunda 
550feeab87bSKiran Gunda 	if (wled->short_count > WLED_SHORT_CNT_MAX) {
551102a1b38SColin Ian King 		dev_err(wled->dev, "Short triggered %d times, disabling WLED forever!\n",
552feeab87bSKiran Gunda 			wled->short_count);
553feeab87bSKiran Gunda 		wled->disabled_by_short = true;
554feeab87bSKiran Gunda 		goto unlock_mutex;
555feeab87bSKiran Gunda 	}
556feeab87bSKiran Gunda 
557feeab87bSKiran Gunda 	wled->last_short_event = ktime_get();
558feeab87bSKiran Gunda 
559feeab87bSKiran Gunda 	msleep(WLED_SHORT_DLY_MS);
560feeab87bSKiran Gunda 	rc = wled_module_enable(wled, true);
561feeab87bSKiran Gunda 	if (rc < 0)
562feeab87bSKiran Gunda 		dev_err(wled->dev, "wled enable failed rc:%d\n", rc);
563feeab87bSKiran Gunda 
564feeab87bSKiran Gunda unlock_mutex:
565feeab87bSKiran Gunda 	mutex_unlock(&wled->lock);
566feeab87bSKiran Gunda 
567feeab87bSKiran Gunda 	return IRQ_HANDLED;
568feeab87bSKiran Gunda }
569feeab87bSKiran Gunda 
5708663c188SKiran Gunda #define AUTO_DETECT_BRIGHTNESS		200
5718663c188SKiran Gunda 
5728663c188SKiran Gunda static void wled_auto_string_detection(struct wled *wled)
5738663c188SKiran Gunda {
574ec961cf3SMarijn Suijten 	int rc = 0, i, j, delay_time_us;
575f16899a6SKiran Gunda 	u32 sink_config = 0;
5768663c188SKiran Gunda 	u8 sink_test = 0, sink_valid = 0, val;
577f16899a6SKiran Gunda 	bool fault_set;
5788663c188SKiran Gunda 
5798663c188SKiran Gunda 	/* Read configured sink configuration */
5808663c188SKiran Gunda 	rc = regmap_read(wled->regmap, wled->sink_addr +
5818663c188SKiran Gunda 			 WLED4_SINK_REG_CURR_SINK, &sink_config);
5828663c188SKiran Gunda 	if (rc < 0) {
5838663c188SKiran Gunda 		dev_err(wled->dev, "Failed to read SINK configuration rc=%d\n",
5848663c188SKiran Gunda 			rc);
5858663c188SKiran Gunda 		goto failed_detect;
5868663c188SKiran Gunda 	}
5878663c188SKiran Gunda 
5888663c188SKiran Gunda 	/* Disable the module before starting detection */
5898663c188SKiran Gunda 	rc = regmap_update_bits(wled->regmap,
5908663c188SKiran Gunda 				wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN,
5918663c188SKiran Gunda 				WLED3_CTRL_REG_MOD_EN_MASK, 0);
5928663c188SKiran Gunda 	if (rc < 0) {
5938663c188SKiran Gunda 		dev_err(wled->dev, "Failed to disable WLED module rc=%d\n", rc);
5948663c188SKiran Gunda 		goto failed_detect;
5958663c188SKiran Gunda 	}
5968663c188SKiran Gunda 
5978663c188SKiran Gunda 	/* Set low brightness across all sinks */
5988663c188SKiran Gunda 	rc = wled4_set_brightness(wled, AUTO_DETECT_BRIGHTNESS);
5998663c188SKiran Gunda 	if (rc < 0) {
6008663c188SKiran Gunda 		dev_err(wled->dev, "Failed to set brightness for auto detection rc=%d\n",
6018663c188SKiran Gunda 			rc);
6028663c188SKiran Gunda 		goto failed_detect;
6038663c188SKiran Gunda 	}
6048663c188SKiran Gunda 
6058663c188SKiran Gunda 	if (wled->cfg.cabc) {
606f16899a6SKiran Gunda 		rc = wled->wled_cabc_config(wled, false);
6078663c188SKiran Gunda 		if (rc < 0)
6088663c188SKiran Gunda 			goto failed_detect;
6098663c188SKiran Gunda 	}
6108663c188SKiran Gunda 
6118663c188SKiran Gunda 	/* Disable all sinks */
6128663c188SKiran Gunda 	rc = regmap_write(wled->regmap,
6138663c188SKiran Gunda 			  wled->sink_addr + WLED4_SINK_REG_CURR_SINK, 0);
6148663c188SKiran Gunda 	if (rc < 0) {
6158663c188SKiran Gunda 		dev_err(wled->dev, "Failed to disable all sinks rc=%d\n", rc);
6168663c188SKiran Gunda 		goto failed_detect;
6178663c188SKiran Gunda 	}
6188663c188SKiran Gunda 
6198663c188SKiran Gunda 	/* Iterate through the strings one by one */
6208663c188SKiran Gunda 	for (i = 0; i < wled->cfg.num_strings; i++) {
621ec961cf3SMarijn Suijten 		j = wled->cfg.enabled_strings[i];
622ec961cf3SMarijn Suijten 		sink_test = BIT((WLED4_SINK_REG_CURR_SINK_SHFT + j));
6238663c188SKiran Gunda 
6248663c188SKiran Gunda 		/* Enable feedback control */
6258663c188SKiran Gunda 		rc = regmap_write(wled->regmap, wled->ctrl_addr +
626ec961cf3SMarijn Suijten 				  WLED3_CTRL_REG_FEEDBACK_CONTROL, j + 1);
6278663c188SKiran Gunda 		if (rc < 0) {
6288663c188SKiran Gunda 			dev_err(wled->dev, "Failed to enable feedback for SINK %d rc = %d\n",
629ec961cf3SMarijn Suijten 				j + 1, rc);
6308663c188SKiran Gunda 			goto failed_detect;
6318663c188SKiran Gunda 		}
6328663c188SKiran Gunda 
6338663c188SKiran Gunda 		/* Enable the sink */
6348663c188SKiran Gunda 		rc = regmap_write(wled->regmap, wled->sink_addr +
6358663c188SKiran Gunda 				  WLED4_SINK_REG_CURR_SINK, sink_test);
6368663c188SKiran Gunda 		if (rc < 0) {
6378663c188SKiran Gunda 			dev_err(wled->dev, "Failed to configure SINK %d rc=%d\n",
638ec961cf3SMarijn Suijten 				j + 1, rc);
6398663c188SKiran Gunda 			goto failed_detect;
6408663c188SKiran Gunda 		}
6418663c188SKiran Gunda 
6428663c188SKiran Gunda 		/* Enable the module */
6438663c188SKiran Gunda 		rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
6448663c188SKiran Gunda 					WLED3_CTRL_REG_MOD_EN,
6458663c188SKiran Gunda 					WLED3_CTRL_REG_MOD_EN_MASK,
6468663c188SKiran Gunda 					WLED3_CTRL_REG_MOD_EN_MASK);
6478663c188SKiran Gunda 		if (rc < 0) {
6488663c188SKiran Gunda 			dev_err(wled->dev, "Failed to enable WLED module rc=%d\n",
6498663c188SKiran Gunda 				rc);
6508663c188SKiran Gunda 			goto failed_detect;
6518663c188SKiran Gunda 		}
6528663c188SKiran Gunda 
653f16899a6SKiran Gunda 		delay_time_us = wled->wled_ovp_delay(wled);
654f16899a6SKiran Gunda 		usleep_range(delay_time_us, delay_time_us + 1000);
6558663c188SKiran Gunda 
656f16899a6SKiran Gunda 		rc = wled_ovp_fault_status(wled, &fault_set);
6578663c188SKiran Gunda 		if (rc < 0) {
658f16899a6SKiran Gunda 			dev_err(wled->dev, "Error in getting OVP fault_sts, rc=%d\n",
6598663c188SKiran Gunda 				rc);
6608663c188SKiran Gunda 			goto failed_detect;
6618663c188SKiran Gunda 		}
6628663c188SKiran Gunda 
663f16899a6SKiran Gunda 		if (fault_set)
6648663c188SKiran Gunda 			dev_dbg(wled->dev, "WLED OVP fault detected with SINK %d\n",
665ec961cf3SMarijn Suijten 				j + 1);
6668663c188SKiran Gunda 		else
6678663c188SKiran Gunda 			sink_valid |= sink_test;
6688663c188SKiran Gunda 
6698663c188SKiran Gunda 		/* Disable the module */
6708663c188SKiran Gunda 		rc = regmap_update_bits(wled->regmap,
6718663c188SKiran Gunda 					wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN,
6728663c188SKiran Gunda 					WLED3_CTRL_REG_MOD_EN_MASK, 0);
6738663c188SKiran Gunda 		if (rc < 0) {
6748663c188SKiran Gunda 			dev_err(wled->dev, "Failed to disable WLED module rc=%d\n",
6758663c188SKiran Gunda 				rc);
6768663c188SKiran Gunda 			goto failed_detect;
6778663c188SKiran Gunda 		}
6788663c188SKiran Gunda 	}
6798663c188SKiran Gunda 
6808663c188SKiran Gunda 	if (!sink_valid) {
6818663c188SKiran Gunda 		dev_err(wled->dev, "No valid WLED sinks found\n");
6828663c188SKiran Gunda 		wled->disabled_by_short = true;
6838663c188SKiran Gunda 		goto failed_detect;
6848663c188SKiran Gunda 	}
6858663c188SKiran Gunda 
6868663c188SKiran Gunda 	if (sink_valid != sink_config) {
6878663c188SKiran Gunda 		dev_warn(wled->dev, "%x is not a valid sink configuration - using %x instead\n",
6888663c188SKiran Gunda 			 sink_config, sink_valid);
6898663c188SKiran Gunda 		sink_config = sink_valid;
6908663c188SKiran Gunda 	}
6918663c188SKiran Gunda 
6928663c188SKiran Gunda 	/* Write the new sink configuration */
6938663c188SKiran Gunda 	rc = regmap_write(wled->regmap,
6948663c188SKiran Gunda 			  wled->sink_addr + WLED4_SINK_REG_CURR_SINK,
6958663c188SKiran Gunda 			  sink_config);
6968663c188SKiran Gunda 	if (rc < 0) {
6978663c188SKiran Gunda 		dev_err(wled->dev, "Failed to reconfigure the default sink rc=%d\n",
6988663c188SKiran Gunda 			rc);
6998663c188SKiran Gunda 		goto failed_detect;
7008663c188SKiran Gunda 	}
7018663c188SKiran Gunda 
7028663c188SKiran Gunda 	/* Enable valid sinks */
703f16899a6SKiran Gunda 	if (wled->version == 4) {
7048663c188SKiran Gunda 		for (i = 0; i < wled->cfg.num_strings; i++) {
705ec961cf3SMarijn Suijten 			j = wled->cfg.enabled_strings[i];
706f16899a6SKiran Gunda 			if (sink_config &
707ec961cf3SMarijn Suijten 			    BIT(WLED4_SINK_REG_CURR_SINK_SHFT + j))
7088663c188SKiran Gunda 				val = WLED4_SINK_REG_STR_MOD_MASK;
7098663c188SKiran Gunda 			else
710f16899a6SKiran Gunda 				/* Disable modulator_en for unused sink */
711f16899a6SKiran Gunda 				val = 0;
7128663c188SKiran Gunda 
7138663c188SKiran Gunda 			rc = regmap_write(wled->regmap, wled->sink_addr +
714ec961cf3SMarijn Suijten 					  WLED4_SINK_REG_STR_MOD_EN(j), val);
7158663c188SKiran Gunda 			if (rc < 0) {
7168663c188SKiran Gunda 				dev_err(wled->dev, "Failed to configure MODULATOR_EN rc=%d\n",
7178663c188SKiran Gunda 					rc);
7188663c188SKiran Gunda 				goto failed_detect;
7198663c188SKiran Gunda 			}
7208663c188SKiran Gunda 		}
721f16899a6SKiran Gunda 	}
722f16899a6SKiran Gunda 
723f16899a6SKiran Gunda 	/* Enable CABC */
724f16899a6SKiran Gunda 	rc = wled->wled_cabc_config(wled, true);
725f16899a6SKiran Gunda 	if (rc < 0)
726f16899a6SKiran Gunda 		goto failed_detect;
7278663c188SKiran Gunda 
7288663c188SKiran Gunda 	/* Restore the feedback setting */
7298663c188SKiran Gunda 	rc = regmap_write(wled->regmap,
7308663c188SKiran Gunda 			  wled->ctrl_addr + WLED3_CTRL_REG_FEEDBACK_CONTROL, 0);
7318663c188SKiran Gunda 	if (rc < 0) {
7328663c188SKiran Gunda 		dev_err(wled->dev, "Failed to restore feedback setting rc=%d\n",
7338663c188SKiran Gunda 			rc);
7348663c188SKiran Gunda 		goto failed_detect;
7358663c188SKiran Gunda 	}
7368663c188SKiran Gunda 
7378663c188SKiran Gunda 	/* Restore brightness */
7388663c188SKiran Gunda 	rc = wled4_set_brightness(wled, wled->brightness);
7398663c188SKiran Gunda 	if (rc < 0) {
7408663c188SKiran Gunda 		dev_err(wled->dev, "Failed to set brightness after auto detection rc=%d\n",
7418663c188SKiran Gunda 			rc);
7428663c188SKiran Gunda 		goto failed_detect;
7438663c188SKiran Gunda 	}
7448663c188SKiran Gunda 
7458663c188SKiran Gunda 	rc = regmap_update_bits(wled->regmap,
7468663c188SKiran Gunda 				wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN,
7478663c188SKiran Gunda 				WLED3_CTRL_REG_MOD_EN_MASK,
7488663c188SKiran Gunda 				WLED3_CTRL_REG_MOD_EN_MASK);
7498663c188SKiran Gunda 	if (rc < 0) {
7508663c188SKiran Gunda 		dev_err(wled->dev, "Failed to enable WLED module rc=%d\n", rc);
7518663c188SKiran Gunda 		goto failed_detect;
7528663c188SKiran Gunda 	}
7538663c188SKiran Gunda 
7548663c188SKiran Gunda failed_detect:
7558663c188SKiran Gunda 	return;
7568663c188SKiran Gunda }
7578663c188SKiran Gunda 
7588663c188SKiran Gunda #define WLED_AUTO_DETECT_OVP_COUNT		5
7598663c188SKiran Gunda #define WLED_AUTO_DETECT_CNT_DLY_US		USEC_PER_SEC
760f16899a6SKiran Gunda 
761f16899a6SKiran Gunda static bool wled4_auto_detection_required(struct wled *wled)
7628663c188SKiran Gunda {
7638663c188SKiran Gunda 	s64 elapsed_time_us;
7648663c188SKiran Gunda 
7658663c188SKiran Gunda 	if (!wled->cfg.auto_detection_enabled)
7668663c188SKiran Gunda 		return false;
7678663c188SKiran Gunda 
7688663c188SKiran Gunda 	/*
7698663c188SKiran Gunda 	 * Check if the OVP fault was an occasional one
7708663c188SKiran Gunda 	 * or if it's firing continuously, the latter qualifies
7718663c188SKiran Gunda 	 * for an auto-detection check.
7728663c188SKiran Gunda 	 */
7738663c188SKiran Gunda 	if (!wled->auto_detection_ovp_count) {
7748663c188SKiran Gunda 		wled->start_ovp_fault_time = ktime_get();
7758663c188SKiran Gunda 		wled->auto_detection_ovp_count++;
7768663c188SKiran Gunda 	} else {
7778663c188SKiran Gunda 		elapsed_time_us = ktime_us_delta(ktime_get(),
7788663c188SKiran Gunda 						 wled->start_ovp_fault_time);
7798663c188SKiran Gunda 		if (elapsed_time_us > WLED_AUTO_DETECT_CNT_DLY_US)
7808663c188SKiran Gunda 			wled->auto_detection_ovp_count = 0;
7818663c188SKiran Gunda 		else
7828663c188SKiran Gunda 			wled->auto_detection_ovp_count++;
7838663c188SKiran Gunda 
7848663c188SKiran Gunda 		if (wled->auto_detection_ovp_count >=
7858663c188SKiran Gunda 				WLED_AUTO_DETECT_OVP_COUNT) {
7868663c188SKiran Gunda 			wled->auto_detection_ovp_count = 0;
7878663c188SKiran Gunda 			return true;
7888663c188SKiran Gunda 		}
7898663c188SKiran Gunda 	}
7908663c188SKiran Gunda 
7918663c188SKiran Gunda 	return false;
7928663c188SKiran Gunda }
7938663c188SKiran Gunda 
79462a1d3f6SSubbaraman Narayanamurthy static bool wled5_auto_detection_required(struct wled *wled)
79562a1d3f6SSubbaraman Narayanamurthy {
79662a1d3f6SSubbaraman Narayanamurthy 	if (!wled->cfg.auto_detection_enabled)
79762a1d3f6SSubbaraman Narayanamurthy 		return false;
79862a1d3f6SSubbaraman Narayanamurthy 
79962a1d3f6SSubbaraman Narayanamurthy 	/*
80062a1d3f6SSubbaraman Narayanamurthy 	 * Unlike WLED4, WLED5 has OVP fault density interrupt configuration
80162a1d3f6SSubbaraman Narayanamurthy 	 * i.e. to count the number of OVP alarms for a certain duration before
80262a1d3f6SSubbaraman Narayanamurthy 	 * triggering OVP fault interrupt. By default, number of OVP fault
80362a1d3f6SSubbaraman Narayanamurthy 	 * events counted before an interrupt is fired is 32 and the time
80462a1d3f6SSubbaraman Narayanamurthy 	 * interval is 12 ms. If we see one OVP fault interrupt, then that
80562a1d3f6SSubbaraman Narayanamurthy 	 * should qualify for a real OVP fault condition to run auto detection
80662a1d3f6SSubbaraman Narayanamurthy 	 * algorithm.
80762a1d3f6SSubbaraman Narayanamurthy 	 */
80862a1d3f6SSubbaraman Narayanamurthy 	return true;
80962a1d3f6SSubbaraman Narayanamurthy }
81062a1d3f6SSubbaraman Narayanamurthy 
8118663c188SKiran Gunda static int wled_auto_detection_at_init(struct wled *wled)
8128663c188SKiran Gunda {
8138663c188SKiran Gunda 	int rc;
814f16899a6SKiran Gunda 	bool fault_set;
8158663c188SKiran Gunda 
8168663c188SKiran Gunda 	if (!wled->cfg.auto_detection_enabled)
8178663c188SKiran Gunda 		return 0;
8188663c188SKiran Gunda 
819f16899a6SKiran Gunda 	rc = wled_ovp_fault_status(wled, &fault_set);
8208663c188SKiran Gunda 	if (rc < 0) {
821f16899a6SKiran Gunda 		dev_err(wled->dev, "Error in getting OVP fault_sts, rc=%d\n",
822f16899a6SKiran Gunda 			rc);
8238663c188SKiran Gunda 		return rc;
8248663c188SKiran Gunda 	}
8258663c188SKiran Gunda 
826f16899a6SKiran Gunda 	if (fault_set) {
8278663c188SKiran Gunda 		mutex_lock(&wled->lock);
8288663c188SKiran Gunda 		wled_auto_string_detection(wled);
8298663c188SKiran Gunda 		mutex_unlock(&wled->lock);
8308663c188SKiran Gunda 	}
8318663c188SKiran Gunda 
8328663c188SKiran Gunda 	return rc;
8338663c188SKiran Gunda }
8348663c188SKiran Gunda 
8358663c188SKiran Gunda static irqreturn_t wled_ovp_irq_handler(int irq, void *_wled)
8368663c188SKiran Gunda {
8378663c188SKiran Gunda 	struct wled *wled = _wled;
8388663c188SKiran Gunda 	int rc;
8398663c188SKiran Gunda 	u32 int_sts, fault_sts;
8408663c188SKiran Gunda 
8418663c188SKiran Gunda 	rc = regmap_read(wled->regmap,
8428663c188SKiran Gunda 			 wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS, &int_sts);
8438663c188SKiran Gunda 	if (rc < 0) {
8448663c188SKiran Gunda 		dev_err(wled->dev, "Error in reading WLED3_INT_RT_STS rc=%d\n",
8458663c188SKiran Gunda 			rc);
8468663c188SKiran Gunda 		return IRQ_HANDLED;
8478663c188SKiran Gunda 	}
8488663c188SKiran Gunda 
8498663c188SKiran Gunda 	rc = regmap_read(wled->regmap, wled->ctrl_addr +
8508663c188SKiran Gunda 			 WLED3_CTRL_REG_FAULT_STATUS, &fault_sts);
8518663c188SKiran Gunda 	if (rc < 0) {
8528663c188SKiran Gunda 		dev_err(wled->dev, "Error in reading WLED_FAULT_STATUS rc=%d\n",
8538663c188SKiran Gunda 			rc);
8548663c188SKiran Gunda 		return IRQ_HANDLED;
8558663c188SKiran Gunda 	}
8568663c188SKiran Gunda 
8578663c188SKiran Gunda 	if (fault_sts & (WLED3_CTRL_REG_OVP_FAULT_BIT |
8588663c188SKiran Gunda 		WLED3_CTRL_REG_ILIM_FAULT_BIT))
8598663c188SKiran Gunda 		dev_dbg(wled->dev, "WLED OVP fault detected, int_sts=%x fault_sts= %x\n",
8608663c188SKiran Gunda 			int_sts, fault_sts);
8618663c188SKiran Gunda 
8628663c188SKiran Gunda 	if (fault_sts & WLED3_CTRL_REG_OVP_FAULT_BIT) {
863f16899a6SKiran Gunda 		if (wled->wled_auto_detection_required(wled)) {
8648663c188SKiran Gunda 			mutex_lock(&wled->lock);
8658663c188SKiran Gunda 			wled_auto_string_detection(wled);
8668663c188SKiran Gunda 			mutex_unlock(&wled->lock);
8678663c188SKiran Gunda 		}
8688663c188SKiran Gunda 	}
8698663c188SKiran Gunda 
8708663c188SKiran Gunda 	return IRQ_HANDLED;
8718663c188SKiran Gunda }
8728663c188SKiran Gunda 
873775d2ffbSKiran Gunda static int wled3_setup(struct wled *wled)
87493c64f1eSCourtney Cavin {
875775d2ffbSKiran Gunda 	u16 addr;
876775d2ffbSKiran Gunda 	u8 sink_en = 0;
877775d2ffbSKiran Gunda 	int rc, i, j;
87893c64f1eSCourtney Cavin 
87993c64f1eSCourtney Cavin 	rc = regmap_update_bits(wled->regmap,
880775d2ffbSKiran Gunda 				wled->ctrl_addr + WLED3_CTRL_REG_OVP,
881bb800a37SKiran Gunda 				WLED3_CTRL_REG_OVP_MASK, wled->cfg.ovp);
88293c64f1eSCourtney Cavin 	if (rc)
88393c64f1eSCourtney Cavin 		return rc;
88493c64f1eSCourtney Cavin 
88593c64f1eSCourtney Cavin 	rc = regmap_update_bits(wled->regmap,
886775d2ffbSKiran Gunda 				wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT,
887775d2ffbSKiran Gunda 				WLED3_CTRL_REG_ILIMIT_MASK,
888775d2ffbSKiran Gunda 				wled->cfg.boost_i_limit);
88993c64f1eSCourtney Cavin 	if (rc)
89093c64f1eSCourtney Cavin 		return rc;
89193c64f1eSCourtney Cavin 
89293c64f1eSCourtney Cavin 	rc = regmap_update_bits(wled->regmap,
893775d2ffbSKiran Gunda 				wled->ctrl_addr + WLED3_CTRL_REG_FREQ,
894775d2ffbSKiran Gunda 				WLED3_CTRL_REG_FREQ_MASK,
895775d2ffbSKiran Gunda 				wled->cfg.switch_freq);
89693c64f1eSCourtney Cavin 	if (rc)
89793c64f1eSCourtney Cavin 		return rc;
89893c64f1eSCourtney Cavin 
89993c64f1eSCourtney Cavin 	for (i = 0; i < wled->cfg.num_strings; ++i) {
900775d2ffbSKiran Gunda 		j = wled->cfg.enabled_strings[i];
901775d2ffbSKiran Gunda 		addr = wled->ctrl_addr + WLED3_SINK_REG_STR_MOD_EN(j);
902775d2ffbSKiran Gunda 		rc = regmap_update_bits(wled->regmap, addr,
903bb800a37SKiran Gunda 					WLED3_SINK_REG_STR_MOD_MASK,
904775d2ffbSKiran Gunda 					WLED3_SINK_REG_STR_MOD_MASK);
90593c64f1eSCourtney Cavin 		if (rc)
90693c64f1eSCourtney Cavin 			return rc;
90793c64f1eSCourtney Cavin 
90893c64f1eSCourtney Cavin 		if (wled->cfg.ext_gen) {
909775d2ffbSKiran Gunda 			addr = wled->ctrl_addr + WLED3_SINK_REG_STR_MOD_SRC(j);
910775d2ffbSKiran Gunda 			rc = regmap_update_bits(wled->regmap, addr,
911bb800a37SKiran Gunda 						WLED3_SINK_REG_STR_MOD_SRC_MASK,
912bb800a37SKiran Gunda 						WLED3_SINK_REG_STR_MOD_SRC_EXT);
91393c64f1eSCourtney Cavin 			if (rc)
91493c64f1eSCourtney Cavin 				return rc;
91593c64f1eSCourtney Cavin 		}
91693c64f1eSCourtney Cavin 
917775d2ffbSKiran Gunda 		addr = wled->ctrl_addr + WLED3_SINK_REG_STR_FULL_SCALE_CURR(j);
918775d2ffbSKiran Gunda 		rc = regmap_update_bits(wled->regmap, addr,
919bb800a37SKiran Gunda 					WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK,
920bb800a37SKiran Gunda 					wled->cfg.string_i_limit);
92193c64f1eSCourtney Cavin 		if (rc)
92293c64f1eSCourtney Cavin 			return rc;
92393c64f1eSCourtney Cavin 
924775d2ffbSKiran Gunda 		addr = wled->ctrl_addr + WLED3_SINK_REG_STR_CABC(j);
925775d2ffbSKiran Gunda 		rc = regmap_update_bits(wled->regmap, addr,
926bb800a37SKiran Gunda 					WLED3_SINK_REG_STR_CABC_MASK,
927775d2ffbSKiran Gunda 					wled->cfg.cabc ?
928775d2ffbSKiran Gunda 					WLED3_SINK_REG_STR_CABC_MASK : 0);
92993c64f1eSCourtney Cavin 		if (rc)
93093c64f1eSCourtney Cavin 			return rc;
931775d2ffbSKiran Gunda 
932775d2ffbSKiran Gunda 		sink_en |= BIT(j + WLED3_SINK_REG_CURR_SINK_SHFT);
93393c64f1eSCourtney Cavin 	}
93493c64f1eSCourtney Cavin 
935775d2ffbSKiran Gunda 	rc = regmap_update_bits(wled->regmap,
936775d2ffbSKiran Gunda 				wled->ctrl_addr + WLED3_SINK_REG_CURR_SINK,
937775d2ffbSKiran Gunda 				WLED3_SINK_REG_CURR_SINK_MASK, sink_en);
938775d2ffbSKiran Gunda 	if (rc)
939775d2ffbSKiran Gunda 		return rc;
940775d2ffbSKiran Gunda 
94193c64f1eSCourtney Cavin 	return 0;
94293c64f1eSCourtney Cavin }
94393c64f1eSCourtney Cavin 
944bb800a37SKiran Gunda static const struct wled_config wled3_config_defaults = {
945bb800a37SKiran Gunda 	.boost_i_limit = 3,
946bb800a37SKiran Gunda 	.string_i_limit = 20,
94793c64f1eSCourtney Cavin 	.ovp = 2,
948775d2ffbSKiran Gunda 	.num_strings = 3,
94993c64f1eSCourtney Cavin 	.switch_freq = 5,
95093c64f1eSCourtney Cavin 	.cs_out_en = false,
95193c64f1eSCourtney Cavin 	.ext_gen = false,
952775d2ffbSKiran Gunda 	.cabc = false,
95396571489SMarijn Suijten 	.enabled_strings = {0, 1, 2},
95493c64f1eSCourtney Cavin };
95593c64f1eSCourtney Cavin 
95603b2b5e8SKiran Gunda static int wled4_setup(struct wled *wled)
95703b2b5e8SKiran Gunda {
95803b2b5e8SKiran Gunda 	int rc, temp, i, j;
95903b2b5e8SKiran Gunda 	u16 addr;
96003b2b5e8SKiran Gunda 	u8 sink_en = 0;
961feeab87bSKiran Gunda 	u32 sink_cfg;
96203b2b5e8SKiran Gunda 
96303b2b5e8SKiran Gunda 	rc = regmap_update_bits(wled->regmap,
96403b2b5e8SKiran Gunda 				wled->ctrl_addr + WLED3_CTRL_REG_OVP,
96503b2b5e8SKiran Gunda 				WLED3_CTRL_REG_OVP_MASK, wled->cfg.ovp);
96603b2b5e8SKiran Gunda 	if (rc < 0)
96703b2b5e8SKiran Gunda 		return rc;
96803b2b5e8SKiran Gunda 
96903b2b5e8SKiran Gunda 	rc = regmap_update_bits(wled->regmap,
97003b2b5e8SKiran Gunda 				wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT,
97103b2b5e8SKiran Gunda 				WLED3_CTRL_REG_ILIMIT_MASK,
97203b2b5e8SKiran Gunda 				wled->cfg.boost_i_limit);
97303b2b5e8SKiran Gunda 	if (rc < 0)
97403b2b5e8SKiran Gunda 		return rc;
97503b2b5e8SKiran Gunda 
97603b2b5e8SKiran Gunda 	rc = regmap_update_bits(wled->regmap,
97703b2b5e8SKiran Gunda 				wled->ctrl_addr + WLED3_CTRL_REG_FREQ,
97803b2b5e8SKiran Gunda 				WLED3_CTRL_REG_FREQ_MASK,
97903b2b5e8SKiran Gunda 				wled->cfg.switch_freq);
98003b2b5e8SKiran Gunda 	if (rc < 0)
98103b2b5e8SKiran Gunda 		return rc;
98203b2b5e8SKiran Gunda 
983feeab87bSKiran Gunda 	if (wled->cfg.external_pfet) {
984feeab87bSKiran Gunda 		/* Unlock the secure register access */
985feeab87bSKiran Gunda 		rc = regmap_write(wled->regmap, wled->ctrl_addr +
986feeab87bSKiran Gunda 				  WLED4_CTRL_REG_SEC_ACCESS,
987feeab87bSKiran Gunda 				  WLED4_CTRL_REG_SEC_UNLOCK);
988feeab87bSKiran Gunda 		if (rc < 0)
989feeab87bSKiran Gunda 			return rc;
990feeab87bSKiran Gunda 
991feeab87bSKiran Gunda 		rc = regmap_write(wled->regmap,
992feeab87bSKiran Gunda 				  wled->ctrl_addr + WLED4_CTRL_REG_TEST1,
993feeab87bSKiran Gunda 				  WLED4_CTRL_REG_TEST1_EXT_FET_DTEST2);
994feeab87bSKiran Gunda 		if (rc < 0)
995feeab87bSKiran Gunda 			return rc;
996feeab87bSKiran Gunda 	}
997feeab87bSKiran Gunda 
99803b2b5e8SKiran Gunda 	rc = regmap_read(wled->regmap, wled->sink_addr +
99903b2b5e8SKiran Gunda 			 WLED4_SINK_REG_CURR_SINK, &sink_cfg);
100003b2b5e8SKiran Gunda 	if (rc < 0)
100103b2b5e8SKiran Gunda 		return rc;
100203b2b5e8SKiran Gunda 
100303b2b5e8SKiran Gunda 	for (i = 0; i < wled->cfg.num_strings; i++) {
100403b2b5e8SKiran Gunda 		j = wled->cfg.enabled_strings[i];
100503b2b5e8SKiran Gunda 		temp = j + WLED4_SINK_REG_CURR_SINK_SHFT;
100603b2b5e8SKiran Gunda 		sink_en |= 1 << temp;
100703b2b5e8SKiran Gunda 	}
100803b2b5e8SKiran Gunda 
10098663c188SKiran Gunda 	if (sink_cfg == sink_en) {
10108663c188SKiran Gunda 		rc = wled_auto_detection_at_init(wled);
10118663c188SKiran Gunda 		return rc;
10128663c188SKiran Gunda 	}
101303b2b5e8SKiran Gunda 
101403b2b5e8SKiran Gunda 	rc = regmap_update_bits(wled->regmap,
101503b2b5e8SKiran Gunda 				wled->sink_addr + WLED4_SINK_REG_CURR_SINK,
101603b2b5e8SKiran Gunda 				WLED4_SINK_REG_CURR_SINK_MASK, 0);
101703b2b5e8SKiran Gunda 	if (rc < 0)
101803b2b5e8SKiran Gunda 		return rc;
101903b2b5e8SKiran Gunda 
102003b2b5e8SKiran Gunda 	rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
102103b2b5e8SKiran Gunda 				WLED3_CTRL_REG_MOD_EN,
102203b2b5e8SKiran Gunda 				WLED3_CTRL_REG_MOD_EN_MASK, 0);
102303b2b5e8SKiran Gunda 	if (rc < 0)
102403b2b5e8SKiran Gunda 		return rc;
102503b2b5e8SKiran Gunda 
102603b2b5e8SKiran Gunda 	/* Per sink/string configuration */
102703b2b5e8SKiran Gunda 	for (i = 0; i < wled->cfg.num_strings; i++) {
102803b2b5e8SKiran Gunda 		j = wled->cfg.enabled_strings[i];
102903b2b5e8SKiran Gunda 
103003b2b5e8SKiran Gunda 		addr = wled->sink_addr +
103103b2b5e8SKiran Gunda 				WLED4_SINK_REG_STR_MOD_EN(j);
103203b2b5e8SKiran Gunda 		rc = regmap_update_bits(wled->regmap, addr,
103303b2b5e8SKiran Gunda 					WLED4_SINK_REG_STR_MOD_MASK,
103403b2b5e8SKiran Gunda 					WLED4_SINK_REG_STR_MOD_MASK);
103503b2b5e8SKiran Gunda 		if (rc < 0)
103603b2b5e8SKiran Gunda 			return rc;
103703b2b5e8SKiran Gunda 
103803b2b5e8SKiran Gunda 		addr = wled->sink_addr +
103903b2b5e8SKiran Gunda 				WLED4_SINK_REG_STR_FULL_SCALE_CURR(j);
104003b2b5e8SKiran Gunda 		rc = regmap_update_bits(wled->regmap, addr,
104103b2b5e8SKiran Gunda 					WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK,
104203b2b5e8SKiran Gunda 					wled->cfg.string_i_limit);
104303b2b5e8SKiran Gunda 		if (rc < 0)
104403b2b5e8SKiran Gunda 			return rc;
1045f16899a6SKiran Gunda 	}
104603b2b5e8SKiran Gunda 
1047f16899a6SKiran Gunda 	rc = wled4_cabc_config(wled, wled->cfg.cabc);
104803b2b5e8SKiran Gunda 	if (rc < 0)
104903b2b5e8SKiran Gunda 		return rc;
105003b2b5e8SKiran Gunda 
105103b2b5e8SKiran Gunda 	rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
105203b2b5e8SKiran Gunda 				WLED3_CTRL_REG_MOD_EN,
105303b2b5e8SKiran Gunda 				WLED3_CTRL_REG_MOD_EN_MASK,
105403b2b5e8SKiran Gunda 				WLED3_CTRL_REG_MOD_EN_MASK);
105503b2b5e8SKiran Gunda 	if (rc < 0)
105603b2b5e8SKiran Gunda 		return rc;
105703b2b5e8SKiran Gunda 
105803b2b5e8SKiran Gunda 	rc = regmap_update_bits(wled->regmap,
105903b2b5e8SKiran Gunda 				wled->sink_addr + WLED4_SINK_REG_CURR_SINK,
106003b2b5e8SKiran Gunda 				WLED4_SINK_REG_CURR_SINK_MASK, sink_en);
106103b2b5e8SKiran Gunda 	if (rc < 0)
106203b2b5e8SKiran Gunda 		return rc;
106303b2b5e8SKiran Gunda 
1064f16899a6SKiran Gunda 	rc = wled->wled_sync_toggle(wled);
106503b2b5e8SKiran Gunda 	if (rc < 0) {
106603b2b5e8SKiran Gunda 		dev_err(wled->dev, "Failed to toggle sync reg rc:%d\n", rc);
106703b2b5e8SKiran Gunda 		return rc;
106803b2b5e8SKiran Gunda 	}
106903b2b5e8SKiran Gunda 
10708663c188SKiran Gunda 	rc = wled_auto_detection_at_init(wled);
10718663c188SKiran Gunda 
10728663c188SKiran Gunda 	return rc;
107303b2b5e8SKiran Gunda }
107403b2b5e8SKiran Gunda 
107503b2b5e8SKiran Gunda static const struct wled_config wled4_config_defaults = {
107603b2b5e8SKiran Gunda 	.boost_i_limit = 4,
107703b2b5e8SKiran Gunda 	.string_i_limit = 10,
107803b2b5e8SKiran Gunda 	.ovp = 1,
107903b2b5e8SKiran Gunda 	.num_strings = 4,
108003b2b5e8SKiran Gunda 	.switch_freq = 11,
108103b2b5e8SKiran Gunda 	.cabc = false,
1082feeab87bSKiran Gunda 	.external_pfet = false,
10838663c188SKiran Gunda 	.auto_detection_enabled = false,
1084c70aefdeSMarijn Suijten 	.enabled_strings = {0, 1, 2, 3},
108503b2b5e8SKiran Gunda };
108603b2b5e8SKiran Gunda 
108762a1d3f6SSubbaraman Narayanamurthy static int wled5_setup(struct wled *wled)
108862a1d3f6SSubbaraman Narayanamurthy {
108962a1d3f6SSubbaraman Narayanamurthy 	int rc, temp, i, j, offset;
109062a1d3f6SSubbaraman Narayanamurthy 	u8 sink_en = 0;
109162a1d3f6SSubbaraman Narayanamurthy 	u16 addr;
109262a1d3f6SSubbaraman Narayanamurthy 	u32 val;
109362a1d3f6SSubbaraman Narayanamurthy 
109462a1d3f6SSubbaraman Narayanamurthy 	rc = regmap_update_bits(wled->regmap,
109562a1d3f6SSubbaraman Narayanamurthy 				wled->ctrl_addr + WLED3_CTRL_REG_OVP,
109662a1d3f6SSubbaraman Narayanamurthy 				WLED5_CTRL_REG_OVP_MASK, wled->cfg.ovp);
109762a1d3f6SSubbaraman Narayanamurthy 	if (rc < 0)
109862a1d3f6SSubbaraman Narayanamurthy 		return rc;
109962a1d3f6SSubbaraman Narayanamurthy 
110062a1d3f6SSubbaraman Narayanamurthy 	rc = regmap_update_bits(wled->regmap,
110162a1d3f6SSubbaraman Narayanamurthy 				wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT,
110262a1d3f6SSubbaraman Narayanamurthy 				WLED3_CTRL_REG_ILIMIT_MASK,
110362a1d3f6SSubbaraman Narayanamurthy 				wled->cfg.boost_i_limit);
110462a1d3f6SSubbaraman Narayanamurthy 	if (rc < 0)
110562a1d3f6SSubbaraman Narayanamurthy 		return rc;
110662a1d3f6SSubbaraman Narayanamurthy 
110762a1d3f6SSubbaraman Narayanamurthy 	rc = regmap_update_bits(wled->regmap,
110862a1d3f6SSubbaraman Narayanamurthy 				wled->ctrl_addr + WLED3_CTRL_REG_FREQ,
110962a1d3f6SSubbaraman Narayanamurthy 				WLED3_CTRL_REG_FREQ_MASK,
111062a1d3f6SSubbaraman Narayanamurthy 				wled->cfg.switch_freq);
111162a1d3f6SSubbaraman Narayanamurthy 	if (rc < 0)
111262a1d3f6SSubbaraman Narayanamurthy 		return rc;
111362a1d3f6SSubbaraman Narayanamurthy 
111462a1d3f6SSubbaraman Narayanamurthy 	/* Per sink/string configuration */
111562a1d3f6SSubbaraman Narayanamurthy 	for (i = 0; i < wled->cfg.num_strings; ++i) {
111662a1d3f6SSubbaraman Narayanamurthy 		j = wled->cfg.enabled_strings[i];
111762a1d3f6SSubbaraman Narayanamurthy 		addr = wled->sink_addr +
111862a1d3f6SSubbaraman Narayanamurthy 				WLED4_SINK_REG_STR_FULL_SCALE_CURR(j);
111962a1d3f6SSubbaraman Narayanamurthy 		rc = regmap_update_bits(wled->regmap, addr,
112062a1d3f6SSubbaraman Narayanamurthy 					WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK,
112162a1d3f6SSubbaraman Narayanamurthy 					wled->cfg.string_i_limit);
112262a1d3f6SSubbaraman Narayanamurthy 		if (rc < 0)
112362a1d3f6SSubbaraman Narayanamurthy 			return rc;
112462a1d3f6SSubbaraman Narayanamurthy 
112562a1d3f6SSubbaraman Narayanamurthy 		addr = wled->sink_addr + WLED5_SINK_REG_STR_SRC_SEL(j);
112662a1d3f6SSubbaraman Narayanamurthy 		rc = regmap_update_bits(wled->regmap, addr,
112762a1d3f6SSubbaraman Narayanamurthy 					WLED5_SINK_REG_SRC_SEL_MASK,
112862a1d3f6SSubbaraman Narayanamurthy 					wled->cfg.mod_sel == MOD_A ?
112962a1d3f6SSubbaraman Narayanamurthy 					WLED5_SINK_REG_SRC_SEL_MOD_A :
113062a1d3f6SSubbaraman Narayanamurthy 					WLED5_SINK_REG_SRC_SEL_MOD_B);
113162a1d3f6SSubbaraman Narayanamurthy 
113262a1d3f6SSubbaraman Narayanamurthy 		temp = j + WLED4_SINK_REG_CURR_SINK_SHFT;
113362a1d3f6SSubbaraman Narayanamurthy 		sink_en |= 1 << temp;
113462a1d3f6SSubbaraman Narayanamurthy 	}
113562a1d3f6SSubbaraman Narayanamurthy 
113662a1d3f6SSubbaraman Narayanamurthy 	rc = wled5_cabc_config(wled, wled->cfg.cabc_sel ? true : false);
113762a1d3f6SSubbaraman Narayanamurthy 	if (rc < 0)
113862a1d3f6SSubbaraman Narayanamurthy 		return rc;
113962a1d3f6SSubbaraman Narayanamurthy 
114062a1d3f6SSubbaraman Narayanamurthy 	/* Enable one of the modulators A or B based on mod_sel */
114162a1d3f6SSubbaraman Narayanamurthy 	addr = wled->sink_addr + WLED5_SINK_REG_MOD_A_EN;
114262a1d3f6SSubbaraman Narayanamurthy 	val = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_REG_MOD_EN_MASK : 0;
114362a1d3f6SSubbaraman Narayanamurthy 	rc = regmap_update_bits(wled->regmap, addr,
114462a1d3f6SSubbaraman Narayanamurthy 				WLED5_SINK_REG_MOD_EN_MASK, val);
114562a1d3f6SSubbaraman Narayanamurthy 	if (rc < 0)
114662a1d3f6SSubbaraman Narayanamurthy 		return rc;
114762a1d3f6SSubbaraman Narayanamurthy 
114862a1d3f6SSubbaraman Narayanamurthy 	addr = wled->sink_addr + WLED5_SINK_REG_MOD_B_EN;
114962a1d3f6SSubbaraman Narayanamurthy 	val = (wled->cfg.mod_sel == MOD_B) ? WLED5_SINK_REG_MOD_EN_MASK : 0;
115062a1d3f6SSubbaraman Narayanamurthy 	rc = regmap_update_bits(wled->regmap, addr,
115162a1d3f6SSubbaraman Narayanamurthy 				WLED5_SINK_REG_MOD_EN_MASK, val);
115262a1d3f6SSubbaraman Narayanamurthy 	if (rc < 0)
115362a1d3f6SSubbaraman Narayanamurthy 		return rc;
115462a1d3f6SSubbaraman Narayanamurthy 
115562a1d3f6SSubbaraman Narayanamurthy 	offset = (wled->cfg.mod_sel == MOD_A) ?
115662a1d3f6SSubbaraman Narayanamurthy 		  WLED5_SINK_REG_MOD_A_BRIGHTNESS_WIDTH_SEL :
115762a1d3f6SSubbaraman Narayanamurthy 		  WLED5_SINK_REG_MOD_B_BRIGHTNESS_WIDTH_SEL;
115862a1d3f6SSubbaraman Narayanamurthy 
115962a1d3f6SSubbaraman Narayanamurthy 	addr = wled->sink_addr + offset;
116062a1d3f6SSubbaraman Narayanamurthy 	val = (wled->max_brightness == WLED5_SINK_REG_BRIGHT_MAX_15B) ?
116162a1d3f6SSubbaraman Narayanamurthy 		 WLED5_SINK_REG_BRIGHTNESS_WIDTH_15B :
116262a1d3f6SSubbaraman Narayanamurthy 		 WLED5_SINK_REG_BRIGHTNESS_WIDTH_12B;
116362a1d3f6SSubbaraman Narayanamurthy 	rc = regmap_write(wled->regmap, addr, val);
116462a1d3f6SSubbaraman Narayanamurthy 	if (rc < 0)
116562a1d3f6SSubbaraman Narayanamurthy 		return rc;
116662a1d3f6SSubbaraman Narayanamurthy 
116762a1d3f6SSubbaraman Narayanamurthy 	rc = regmap_update_bits(wled->regmap,
116862a1d3f6SSubbaraman Narayanamurthy 				wled->sink_addr + WLED4_SINK_REG_CURR_SINK,
116962a1d3f6SSubbaraman Narayanamurthy 				WLED4_SINK_REG_CURR_SINK_MASK, sink_en);
117062a1d3f6SSubbaraman Narayanamurthy 	if (rc < 0)
117162a1d3f6SSubbaraman Narayanamurthy 		return rc;
117262a1d3f6SSubbaraman Narayanamurthy 
117362a1d3f6SSubbaraman Narayanamurthy 	/* This updates only FSC configuration in WLED5 */
117462a1d3f6SSubbaraman Narayanamurthy 	rc = wled->wled_sync_toggle(wled);
117562a1d3f6SSubbaraman Narayanamurthy 	if (rc < 0) {
117662a1d3f6SSubbaraman Narayanamurthy 		pr_err("Failed to toggle sync reg rc:%d\n", rc);
117762a1d3f6SSubbaraman Narayanamurthy 		return rc;
117862a1d3f6SSubbaraman Narayanamurthy 	}
117962a1d3f6SSubbaraman Narayanamurthy 
118062a1d3f6SSubbaraman Narayanamurthy 	rc = wled_auto_detection_at_init(wled);
118162a1d3f6SSubbaraman Narayanamurthy 	if (rc < 0)
118262a1d3f6SSubbaraman Narayanamurthy 		return rc;
118362a1d3f6SSubbaraman Narayanamurthy 
118462a1d3f6SSubbaraman Narayanamurthy 	return 0;
118562a1d3f6SSubbaraman Narayanamurthy }
118662a1d3f6SSubbaraman Narayanamurthy 
118762a1d3f6SSubbaraman Narayanamurthy static const struct wled_config wled5_config_defaults = {
118862a1d3f6SSubbaraman Narayanamurthy 	.boost_i_limit = 5,
118962a1d3f6SSubbaraman Narayanamurthy 	.string_i_limit = 10,
119062a1d3f6SSubbaraman Narayanamurthy 	.ovp = 4,
119162a1d3f6SSubbaraman Narayanamurthy 	.num_strings = 4,
119262a1d3f6SSubbaraman Narayanamurthy 	.switch_freq = 11,
119362a1d3f6SSubbaraman Narayanamurthy 	.mod_sel = 0,
119462a1d3f6SSubbaraman Narayanamurthy 	.cabc_sel = 0,
119562a1d3f6SSubbaraman Narayanamurthy 	.cabc = false,
119662a1d3f6SSubbaraman Narayanamurthy 	.external_pfet = false,
119762a1d3f6SSubbaraman Narayanamurthy 	.auto_detection_enabled = false,
1198c70aefdeSMarijn Suijten 	.enabled_strings = {0, 1, 2, 3},
119962a1d3f6SSubbaraman Narayanamurthy };
120062a1d3f6SSubbaraman Narayanamurthy 
1201bb800a37SKiran Gunda static const u32 wled3_boost_i_limit_values[] = {
120293c64f1eSCourtney Cavin 	105, 385, 525, 805, 980, 1260, 1400, 1680,
120393c64f1eSCourtney Cavin };
120493c64f1eSCourtney Cavin 
1205bb800a37SKiran Gunda static const struct wled_var_cfg wled3_boost_i_limit_cfg = {
1206bb800a37SKiran Gunda 	.values = wled3_boost_i_limit_values,
1207bb800a37SKiran Gunda 	.size = ARRAY_SIZE(wled3_boost_i_limit_values),
120893c64f1eSCourtney Cavin };
120993c64f1eSCourtney Cavin 
121003b2b5e8SKiran Gunda static const u32 wled4_boost_i_limit_values[] = {
121103b2b5e8SKiran Gunda 	105, 280, 450, 620, 970, 1150, 1300, 1500,
121203b2b5e8SKiran Gunda };
121303b2b5e8SKiran Gunda 
121403b2b5e8SKiran Gunda static const struct wled_var_cfg wled4_boost_i_limit_cfg = {
121503b2b5e8SKiran Gunda 	.values = wled4_boost_i_limit_values,
121603b2b5e8SKiran Gunda 	.size = ARRAY_SIZE(wled4_boost_i_limit_values),
121703b2b5e8SKiran Gunda };
121803b2b5e8SKiran Gunda 
121962a1d3f6SSubbaraman Narayanamurthy static inline u32 wled5_boost_i_limit_values_fn(u32 idx)
122062a1d3f6SSubbaraman Narayanamurthy {
122162a1d3f6SSubbaraman Narayanamurthy 	return 525 + (idx * 175);
122262a1d3f6SSubbaraman Narayanamurthy }
122362a1d3f6SSubbaraman Narayanamurthy 
122462a1d3f6SSubbaraman Narayanamurthy static const struct wled_var_cfg wled5_boost_i_limit_cfg = {
122562a1d3f6SSubbaraman Narayanamurthy 	.fn = wled5_boost_i_limit_values_fn,
122662a1d3f6SSubbaraman Narayanamurthy 	.size = 8,
122762a1d3f6SSubbaraman Narayanamurthy };
122862a1d3f6SSubbaraman Narayanamurthy 
1229bb800a37SKiran Gunda static const u32 wled3_ovp_values[] = {
123093c64f1eSCourtney Cavin 	35, 32, 29, 27,
123193c64f1eSCourtney Cavin };
123293c64f1eSCourtney Cavin 
1233bb800a37SKiran Gunda static const struct wled_var_cfg wled3_ovp_cfg = {
1234bb800a37SKiran Gunda 	.values = wled3_ovp_values,
1235bb800a37SKiran Gunda 	.size = ARRAY_SIZE(wled3_ovp_values),
123693c64f1eSCourtney Cavin };
123793c64f1eSCourtney Cavin 
123803b2b5e8SKiran Gunda static const u32 wled4_ovp_values[] = {
123903b2b5e8SKiran Gunda 	31100, 29600, 19600, 18100,
124003b2b5e8SKiran Gunda };
124103b2b5e8SKiran Gunda 
124203b2b5e8SKiran Gunda static const struct wled_var_cfg wled4_ovp_cfg = {
124303b2b5e8SKiran Gunda 	.values = wled4_ovp_values,
124403b2b5e8SKiran Gunda 	.size = ARRAY_SIZE(wled4_ovp_values),
124503b2b5e8SKiran Gunda };
124603b2b5e8SKiran Gunda 
124762a1d3f6SSubbaraman Narayanamurthy static inline u32 wled5_ovp_values_fn(u32 idx)
124862a1d3f6SSubbaraman Narayanamurthy {
124962a1d3f6SSubbaraman Narayanamurthy 	/*
125062a1d3f6SSubbaraman Narayanamurthy 	 * 0000 - 38.5 V
125162a1d3f6SSubbaraman Narayanamurthy 	 * 0001 - 37 V ..
125262a1d3f6SSubbaraman Narayanamurthy 	 * 1111 - 16 V
125362a1d3f6SSubbaraman Narayanamurthy 	 */
125462a1d3f6SSubbaraman Narayanamurthy 	return 38500 - (idx * 1500);
125562a1d3f6SSubbaraman Narayanamurthy }
125662a1d3f6SSubbaraman Narayanamurthy 
125762a1d3f6SSubbaraman Narayanamurthy static const struct wled_var_cfg wled5_ovp_cfg = {
125862a1d3f6SSubbaraman Narayanamurthy 	.fn = wled5_ovp_values_fn,
125962a1d3f6SSubbaraman Narayanamurthy 	.size = 16,
126062a1d3f6SSubbaraman Narayanamurthy };
126162a1d3f6SSubbaraman Narayanamurthy 
1262bb800a37SKiran Gunda static u32 wled3_switch_freq_values_fn(u32 idx)
126393c64f1eSCourtney Cavin {
126493c64f1eSCourtney Cavin 	return 19200 / (2 * (1 + idx));
126593c64f1eSCourtney Cavin }
126693c64f1eSCourtney Cavin 
1267bb800a37SKiran Gunda static const struct wled_var_cfg wled3_switch_freq_cfg = {
1268bb800a37SKiran Gunda 	.fn = wled3_switch_freq_values_fn,
126993c64f1eSCourtney Cavin 	.size = 16,
127093c64f1eSCourtney Cavin };
127193c64f1eSCourtney Cavin 
1272bb800a37SKiran Gunda static const struct wled_var_cfg wled3_string_i_limit_cfg = {
127393c64f1eSCourtney Cavin 	.size = 26,
127493c64f1eSCourtney Cavin };
127593c64f1eSCourtney Cavin 
127603b2b5e8SKiran Gunda static const u32 wled4_string_i_limit_values[] = {
127703b2b5e8SKiran Gunda 	0, 2500, 5000, 7500, 10000, 12500, 15000, 17500, 20000,
127803b2b5e8SKiran Gunda 	22500, 25000, 27500, 30000,
127903b2b5e8SKiran Gunda };
128003b2b5e8SKiran Gunda 
128103b2b5e8SKiran Gunda static const struct wled_var_cfg wled4_string_i_limit_cfg = {
128203b2b5e8SKiran Gunda 	.values = wled4_string_i_limit_values,
128303b2b5e8SKiran Gunda 	.size = ARRAY_SIZE(wled4_string_i_limit_values),
128403b2b5e8SKiran Gunda };
128503b2b5e8SKiran Gunda 
128662a1d3f6SSubbaraman Narayanamurthy static const struct wled_var_cfg wled5_mod_sel_cfg = {
128762a1d3f6SSubbaraman Narayanamurthy 	.size = 2,
128862a1d3f6SSubbaraman Narayanamurthy };
128962a1d3f6SSubbaraman Narayanamurthy 
129062a1d3f6SSubbaraman Narayanamurthy static const struct wled_var_cfg wled5_cabc_sel_cfg = {
129162a1d3f6SSubbaraman Narayanamurthy 	.size = 4,
129262a1d3f6SSubbaraman Narayanamurthy };
129362a1d3f6SSubbaraman Narayanamurthy 
1294775d2ffbSKiran Gunda static u32 wled_values(const struct wled_var_cfg *cfg, u32 idx)
129593c64f1eSCourtney Cavin {
129693c64f1eSCourtney Cavin 	if (idx >= cfg->size)
129793c64f1eSCourtney Cavin 		return UINT_MAX;
129893c64f1eSCourtney Cavin 	if (cfg->fn)
129993c64f1eSCourtney Cavin 		return cfg->fn(idx);
130093c64f1eSCourtney Cavin 	if (cfg->values)
130193c64f1eSCourtney Cavin 		return cfg->values[idx];
130293c64f1eSCourtney Cavin 	return idx;
130393c64f1eSCourtney Cavin }
130493c64f1eSCourtney Cavin 
1305f16899a6SKiran Gunda static int wled_configure(struct wled *wled)
130693c64f1eSCourtney Cavin {
1307bb800a37SKiran Gunda 	struct wled_config *cfg = &wled->cfg;
1308775d2ffbSKiran Gunda 	struct device *dev = wled->dev;
1309775d2ffbSKiran Gunda 	const __be32 *prop_addr;
13107af43a76SChen Zhou 	u32 size, val, c;
13117af43a76SChen Zhou 	int rc, i, j, string_len;
131293c64f1eSCourtney Cavin 
1313775d2ffbSKiran Gunda 	const struct wled_u32_opts *u32_opts = NULL;
1314775d2ffbSKiran Gunda 	const struct wled_u32_opts wled3_opts[] = {
131593c64f1eSCourtney Cavin 		{
1316775d2ffbSKiran Gunda 			.name = "qcom,current-boost-limit",
1317775d2ffbSKiran Gunda 			.val_ptr = &cfg->boost_i_limit,
1318bb800a37SKiran Gunda 			.cfg = &wled3_boost_i_limit_cfg,
131993c64f1eSCourtney Cavin 		},
132093c64f1eSCourtney Cavin 		{
1321775d2ffbSKiran Gunda 			.name = "qcom,current-limit",
1322775d2ffbSKiran Gunda 			.val_ptr = &cfg->string_i_limit,
1323bb800a37SKiran Gunda 			.cfg = &wled3_string_i_limit_cfg,
132493c64f1eSCourtney Cavin 		},
132593c64f1eSCourtney Cavin 		{
1326775d2ffbSKiran Gunda 			.name = "qcom,ovp",
1327775d2ffbSKiran Gunda 			.val_ptr = &cfg->ovp,
1328bb800a37SKiran Gunda 			.cfg = &wled3_ovp_cfg,
132993c64f1eSCourtney Cavin 		},
133093c64f1eSCourtney Cavin 		{
1331775d2ffbSKiran Gunda 			.name = "qcom,switching-freq",
1332775d2ffbSKiran Gunda 			.val_ptr = &cfg->switch_freq,
1333bb800a37SKiran Gunda 			.cfg = &wled3_switch_freq_cfg,
133493c64f1eSCourtney Cavin 		},
133593c64f1eSCourtney Cavin 	};
1336775d2ffbSKiran Gunda 
133703b2b5e8SKiran Gunda 	const struct wled_u32_opts wled4_opts[] = {
133803b2b5e8SKiran Gunda 		{
133903b2b5e8SKiran Gunda 			.name = "qcom,current-boost-limit",
134003b2b5e8SKiran Gunda 			.val_ptr = &cfg->boost_i_limit,
134103b2b5e8SKiran Gunda 			.cfg = &wled4_boost_i_limit_cfg,
134203b2b5e8SKiran Gunda 		},
134303b2b5e8SKiran Gunda 		{
134403b2b5e8SKiran Gunda 			.name = "qcom,current-limit-microamp",
134503b2b5e8SKiran Gunda 			.val_ptr = &cfg->string_i_limit,
134603b2b5e8SKiran Gunda 			.cfg = &wled4_string_i_limit_cfg,
134703b2b5e8SKiran Gunda 		},
134803b2b5e8SKiran Gunda 		{
134903b2b5e8SKiran Gunda 			.name = "qcom,ovp-millivolt",
135003b2b5e8SKiran Gunda 			.val_ptr = &cfg->ovp,
135103b2b5e8SKiran Gunda 			.cfg = &wled4_ovp_cfg,
135203b2b5e8SKiran Gunda 		},
135303b2b5e8SKiran Gunda 		{
135403b2b5e8SKiran Gunda 			.name = "qcom,switching-freq",
135503b2b5e8SKiran Gunda 			.val_ptr = &cfg->switch_freq,
135603b2b5e8SKiran Gunda 			.cfg = &wled3_switch_freq_cfg,
135703b2b5e8SKiran Gunda 		},
135803b2b5e8SKiran Gunda 	};
135903b2b5e8SKiran Gunda 
136062a1d3f6SSubbaraman Narayanamurthy 	const struct wled_u32_opts wled5_opts[] = {
136162a1d3f6SSubbaraman Narayanamurthy 		{
136262a1d3f6SSubbaraman Narayanamurthy 			.name = "qcom,current-boost-limit",
136362a1d3f6SSubbaraman Narayanamurthy 			.val_ptr = &cfg->boost_i_limit,
136462a1d3f6SSubbaraman Narayanamurthy 			.cfg = &wled5_boost_i_limit_cfg,
136562a1d3f6SSubbaraman Narayanamurthy 		},
136662a1d3f6SSubbaraman Narayanamurthy 		{
136762a1d3f6SSubbaraman Narayanamurthy 			.name = "qcom,current-limit-microamp",
136862a1d3f6SSubbaraman Narayanamurthy 			.val_ptr = &cfg->string_i_limit,
136962a1d3f6SSubbaraman Narayanamurthy 			.cfg = &wled4_string_i_limit_cfg,
137062a1d3f6SSubbaraman Narayanamurthy 		},
137162a1d3f6SSubbaraman Narayanamurthy 		{
137262a1d3f6SSubbaraman Narayanamurthy 			.name = "qcom,ovp-millivolt",
137362a1d3f6SSubbaraman Narayanamurthy 			.val_ptr = &cfg->ovp,
137462a1d3f6SSubbaraman Narayanamurthy 			.cfg = &wled5_ovp_cfg,
137562a1d3f6SSubbaraman Narayanamurthy 		},
137662a1d3f6SSubbaraman Narayanamurthy 		{
137762a1d3f6SSubbaraman Narayanamurthy 			.name = "qcom,switching-freq",
137862a1d3f6SSubbaraman Narayanamurthy 			.val_ptr = &cfg->switch_freq,
137962a1d3f6SSubbaraman Narayanamurthy 			.cfg = &wled3_switch_freq_cfg,
138062a1d3f6SSubbaraman Narayanamurthy 		},
138162a1d3f6SSubbaraman Narayanamurthy 		{
138262a1d3f6SSubbaraman Narayanamurthy 			.name = "qcom,modulator-sel",
138362a1d3f6SSubbaraman Narayanamurthy 			.val_ptr = &cfg->mod_sel,
138462a1d3f6SSubbaraman Narayanamurthy 			.cfg = &wled5_mod_sel_cfg,
138562a1d3f6SSubbaraman Narayanamurthy 		},
138662a1d3f6SSubbaraman Narayanamurthy 		{
138762a1d3f6SSubbaraman Narayanamurthy 			.name = "qcom,cabc-sel",
138862a1d3f6SSubbaraman Narayanamurthy 			.val_ptr = &cfg->cabc_sel,
138962a1d3f6SSubbaraman Narayanamurthy 			.cfg = &wled5_cabc_sel_cfg,
139062a1d3f6SSubbaraman Narayanamurthy 		},
139162a1d3f6SSubbaraman Narayanamurthy 	};
139262a1d3f6SSubbaraman Narayanamurthy 
1393775d2ffbSKiran Gunda 	const struct wled_bool_opts bool_opts[] = {
139493c64f1eSCourtney Cavin 		{ "qcom,cs-out", &cfg->cs_out_en, },
139593c64f1eSCourtney Cavin 		{ "qcom,ext-gen", &cfg->ext_gen, },
1396775d2ffbSKiran Gunda 		{ "qcom,cabc", &cfg->cabc, },
1397feeab87bSKiran Gunda 		{ "qcom,external-pfet", &cfg->external_pfet, },
13988663c188SKiran Gunda 		{ "qcom,auto-string-detection", &cfg->auto_detection_enabled, },
139993c64f1eSCourtney Cavin 	};
140093c64f1eSCourtney Cavin 
1401775d2ffbSKiran Gunda 	prop_addr = of_get_address(dev->of_node, 0, NULL, NULL);
1402775d2ffbSKiran Gunda 	if (!prop_addr) {
1403775d2ffbSKiran Gunda 		dev_err(wled->dev, "invalid IO resources\n");
1404775d2ffbSKiran Gunda 		return -EINVAL;
140593c64f1eSCourtney Cavin 	}
1406775d2ffbSKiran Gunda 	wled->ctrl_addr = be32_to_cpu(*prop_addr);
140793c64f1eSCourtney Cavin 
14087ddbc242SBjorn Andersson 	rc = of_property_read_string(dev->of_node, "label", &wled->name);
140993c64f1eSCourtney Cavin 	if (rc)
1410f86b7758SRob Herring 		wled->name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node);
141193c64f1eSCourtney Cavin 
1412f16899a6SKiran Gunda 	switch (wled->version) {
1413775d2ffbSKiran Gunda 	case 3:
1414775d2ffbSKiran Gunda 		u32_opts = wled3_opts;
1415775d2ffbSKiran Gunda 		size = ARRAY_SIZE(wled3_opts);
1416bb800a37SKiran Gunda 		*cfg = wled3_config_defaults;
1417775d2ffbSKiran Gunda 		wled->wled_set_brightness = wled3_set_brightness;
1418f16899a6SKiran Gunda 		wled->wled_sync_toggle = wled3_sync_toggle;
1419775d2ffbSKiran Gunda 		wled->max_string_count = 3;
142003b2b5e8SKiran Gunda 		wled->sink_addr = wled->ctrl_addr;
142103b2b5e8SKiran Gunda 		break;
142203b2b5e8SKiran Gunda 
142303b2b5e8SKiran Gunda 	case 4:
142403b2b5e8SKiran Gunda 		u32_opts = wled4_opts;
142503b2b5e8SKiran Gunda 		size = ARRAY_SIZE(wled4_opts);
142603b2b5e8SKiran Gunda 		*cfg = wled4_config_defaults;
142703b2b5e8SKiran Gunda 		wled->wled_set_brightness = wled4_set_brightness;
1428f16899a6SKiran Gunda 		wled->wled_sync_toggle = wled3_sync_toggle;
1429f16899a6SKiran Gunda 		wled->wled_cabc_config = wled4_cabc_config;
1430f16899a6SKiran Gunda 		wled->wled_ovp_delay = wled4_ovp_delay;
1431f16899a6SKiran Gunda 		wled->wled_auto_detection_required =
1432f16899a6SKiran Gunda 					wled4_auto_detection_required;
143303b2b5e8SKiran Gunda 		wled->max_string_count = 4;
143403b2b5e8SKiran Gunda 
143503b2b5e8SKiran Gunda 		prop_addr = of_get_address(dev->of_node, 1, NULL, NULL);
143603b2b5e8SKiran Gunda 		if (!prop_addr) {
143703b2b5e8SKiran Gunda 			dev_err(wled->dev, "invalid IO resources\n");
143803b2b5e8SKiran Gunda 			return -EINVAL;
143903b2b5e8SKiran Gunda 		}
144003b2b5e8SKiran Gunda 		wled->sink_addr = be32_to_cpu(*prop_addr);
1441775d2ffbSKiran Gunda 		break;
1442775d2ffbSKiran Gunda 
144362a1d3f6SSubbaraman Narayanamurthy 	case 5:
144462a1d3f6SSubbaraman Narayanamurthy 		u32_opts = wled5_opts;
144562a1d3f6SSubbaraman Narayanamurthy 		size = ARRAY_SIZE(wled5_opts);
144662a1d3f6SSubbaraman Narayanamurthy 		*cfg = wled5_config_defaults;
144762a1d3f6SSubbaraman Narayanamurthy 		wled->wled_set_brightness = wled5_set_brightness;
14484d6e9cdfSKiran Gunda 		wled->wled_sync_toggle = wled3_sync_toggle;
144962a1d3f6SSubbaraman Narayanamurthy 		wled->wled_cabc_config = wled5_cabc_config;
145062a1d3f6SSubbaraman Narayanamurthy 		wled->wled_ovp_delay = wled5_ovp_delay;
145162a1d3f6SSubbaraman Narayanamurthy 		wled->wled_auto_detection_required =
145262a1d3f6SSubbaraman Narayanamurthy 					wled5_auto_detection_required;
145362a1d3f6SSubbaraman Narayanamurthy 		wled->max_string_count = 4;
145462a1d3f6SSubbaraman Narayanamurthy 
145562a1d3f6SSubbaraman Narayanamurthy 		prop_addr = of_get_address(dev->of_node, 1, NULL, NULL);
145662a1d3f6SSubbaraman Narayanamurthy 		if (!prop_addr) {
145762a1d3f6SSubbaraman Narayanamurthy 			dev_err(wled->dev, "invalid IO resources\n");
145862a1d3f6SSubbaraman Narayanamurthy 			return -EINVAL;
145962a1d3f6SSubbaraman Narayanamurthy 		}
146062a1d3f6SSubbaraman Narayanamurthy 		wled->sink_addr = be32_to_cpu(*prop_addr);
146162a1d3f6SSubbaraman Narayanamurthy 		break;
146262a1d3f6SSubbaraman Narayanamurthy 
1463775d2ffbSKiran Gunda 	default:
1464775d2ffbSKiran Gunda 		dev_err(wled->dev, "Invalid WLED version\n");
1465775d2ffbSKiran Gunda 		return -EINVAL;
1466775d2ffbSKiran Gunda 	}
1467775d2ffbSKiran Gunda 
1468775d2ffbSKiran Gunda 	for (i = 0; i < size; ++i) {
146993c64f1eSCourtney Cavin 		rc = of_property_read_u32(dev->of_node, u32_opts[i].name, &val);
147093c64f1eSCourtney Cavin 		if (rc == -EINVAL) {
147193c64f1eSCourtney Cavin 			continue;
147293c64f1eSCourtney Cavin 		} else if (rc) {
147393c64f1eSCourtney Cavin 			dev_err(dev, "error reading '%s'\n", u32_opts[i].name);
147493c64f1eSCourtney Cavin 			return rc;
147593c64f1eSCourtney Cavin 		}
147693c64f1eSCourtney Cavin 
147793c64f1eSCourtney Cavin 		c = UINT_MAX;
147893c64f1eSCourtney Cavin 		for (j = 0; c != val; j++) {
1479775d2ffbSKiran Gunda 			c = wled_values(u32_opts[i].cfg, j);
148093c64f1eSCourtney Cavin 			if (c == UINT_MAX) {
148193c64f1eSCourtney Cavin 				dev_err(dev, "invalid value for '%s'\n",
148293c64f1eSCourtney Cavin 					u32_opts[i].name);
148393c64f1eSCourtney Cavin 				return -EINVAL;
148493c64f1eSCourtney Cavin 			}
1485775d2ffbSKiran Gunda 
1486775d2ffbSKiran Gunda 			if (c == val)
1487775d2ffbSKiran Gunda 				break;
148893c64f1eSCourtney Cavin 		}
148993c64f1eSCourtney Cavin 
149093c64f1eSCourtney Cavin 		dev_dbg(dev, "'%s' = %u\n", u32_opts[i].name, c);
149193c64f1eSCourtney Cavin 		*u32_opts[i].val_ptr = j;
149293c64f1eSCourtney Cavin 	}
149393c64f1eSCourtney Cavin 
149493c64f1eSCourtney Cavin 	for (i = 0; i < ARRAY_SIZE(bool_opts); ++i) {
149593c64f1eSCourtney Cavin 		if (of_property_read_bool(dev->of_node, bool_opts[i].name))
149693c64f1eSCourtney Cavin 			*bool_opts[i].val_ptr = true;
149793c64f1eSCourtney Cavin 	}
149893c64f1eSCourtney Cavin 
1499775d2ffbSKiran Gunda 	string_len = of_property_count_elems_of_size(dev->of_node,
1500775d2ffbSKiran Gunda 						     "qcom,enabled-strings",
1501775d2ffbSKiran Gunda 						     sizeof(u32));
1502c05b21ebSMarijn Suijten 	if (string_len > 0) {
1503c05b21ebSMarijn Suijten 		if (string_len > wled->max_string_count) {
1504c05b21ebSMarijn Suijten 			dev_err(dev, "Cannot have more than %d strings\n",
1505c05b21ebSMarijn Suijten 				wled->max_string_count);
1506c05b21ebSMarijn Suijten 			return -EINVAL;
1507c05b21ebSMarijn Suijten 		}
1508c05b21ebSMarijn Suijten 
1509e29e24bdSMarijn Suijten 		rc = of_property_read_u32_array(dev->of_node,
1510775d2ffbSKiran Gunda 						"qcom,enabled-strings",
1511775d2ffbSKiran Gunda 						wled->cfg.enabled_strings,
1512e29e24bdSMarijn Suijten 						string_len);
1513e29e24bdSMarijn Suijten 		if (rc) {
1514e29e24bdSMarijn Suijten 			dev_err(dev, "Failed to read %d elements from qcom,enabled-strings: %d\n",
1515e29e24bdSMarijn Suijten 				string_len, rc);
1516e29e24bdSMarijn Suijten 			return rc;
1517e29e24bdSMarijn Suijten 		}
1518775d2ffbSKiran Gunda 
1519c05b21ebSMarijn Suijten 		for (i = 0; i < string_len; ++i) {
1520c05b21ebSMarijn Suijten 			if (wled->cfg.enabled_strings[i] >= wled->max_string_count) {
1521c05b21ebSMarijn Suijten 				dev_err(dev,
1522c05b21ebSMarijn Suijten 					"qcom,enabled-strings index %d at %d is out of bounds\n",
1523c05b21ebSMarijn Suijten 					wled->cfg.enabled_strings[i], i);
1524c05b21ebSMarijn Suijten 				return -EINVAL;
1525c05b21ebSMarijn Suijten 			}
1526c05b21ebSMarijn Suijten 		}
15272b4b4960SMarijn Suijten 
15282b4b4960SMarijn Suijten 		cfg->num_strings = string_len;
1529c05b21ebSMarijn Suijten 	}
1530c05b21ebSMarijn Suijten 
15315ada78b2SMarijn Suijten 	rc = of_property_read_u32(dev->of_node, "qcom,num-strings", &val);
15325ada78b2SMarijn Suijten 	if (!rc) {
15335ada78b2SMarijn Suijten 		if (val < 1 || val > wled->max_string_count) {
15345ada78b2SMarijn Suijten 			dev_err(dev, "qcom,num-strings must be between 1 and %d\n",
15355ada78b2SMarijn Suijten 				wled->max_string_count);
15365ada78b2SMarijn Suijten 			return -EINVAL;
15375ada78b2SMarijn Suijten 		}
15385ada78b2SMarijn Suijten 
15392b4b4960SMarijn Suijten 		if (string_len > 0) {
15402b4b4960SMarijn Suijten 			dev_warn(dev, "Only one of qcom,num-strings or qcom,enabled-strings"
15412b4b4960SMarijn Suijten 				      " should be set\n");
15422b4b4960SMarijn Suijten 			if (val > string_len) {
15435ada78b2SMarijn Suijten 				dev_err(dev, "qcom,num-strings exceeds qcom,enabled-strings\n");
15445ada78b2SMarijn Suijten 				return -EINVAL;
15455ada78b2SMarijn Suijten 			}
15462b4b4960SMarijn Suijten 		}
15475ada78b2SMarijn Suijten 
15485ada78b2SMarijn Suijten 		cfg->num_strings = val;
15495ada78b2SMarijn Suijten 	}
15505ada78b2SMarijn Suijten 
155193c64f1eSCourtney Cavin 	return 0;
155293c64f1eSCourtney Cavin }
155393c64f1eSCourtney Cavin 
1554feeab87bSKiran Gunda static int wled_configure_short_irq(struct wled *wled,
1555feeab87bSKiran Gunda 				    struct platform_device *pdev)
1556feeab87bSKiran Gunda {
1557feeab87bSKiran Gunda 	int rc;
1558feeab87bSKiran Gunda 
1559feeab87bSKiran Gunda 	if (!wled->has_short_detect)
1560feeab87bSKiran Gunda 		return 0;
1561feeab87bSKiran Gunda 
1562feeab87bSKiran Gunda 	rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
1563feeab87bSKiran Gunda 				WLED4_CTRL_REG_SHORT_PROTECT,
1564feeab87bSKiran Gunda 				WLED4_CTRL_REG_SHORT_EN_MASK,
1565feeab87bSKiran Gunda 				WLED4_CTRL_REG_SHORT_EN_MASK);
1566feeab87bSKiran Gunda 	if (rc < 0)
1567feeab87bSKiran Gunda 		return rc;
1568feeab87bSKiran Gunda 
1569feeab87bSKiran Gunda 	wled->short_irq = platform_get_irq_byname(pdev, "short");
1570feeab87bSKiran Gunda 	if (wled->short_irq < 0) {
1571feeab87bSKiran Gunda 		dev_dbg(&pdev->dev, "short irq is not used\n");
1572feeab87bSKiran Gunda 		return 0;
1573feeab87bSKiran Gunda 	}
1574feeab87bSKiran Gunda 
1575feeab87bSKiran Gunda 	rc = devm_request_threaded_irq(wled->dev, wled->short_irq,
1576feeab87bSKiran Gunda 				       NULL, wled_short_irq_handler,
1577feeab87bSKiran Gunda 				       IRQF_ONESHOT,
1578feeab87bSKiran Gunda 				       "wled_short_irq", wled);
1579feeab87bSKiran Gunda 	if (rc < 0)
1580feeab87bSKiran Gunda 		dev_err(wled->dev, "Unable to request short_irq (err:%d)\n",
1581feeab87bSKiran Gunda 			rc);
1582feeab87bSKiran Gunda 
1583feeab87bSKiran Gunda 	return rc;
1584feeab87bSKiran Gunda }
1585feeab87bSKiran Gunda 
15868663c188SKiran Gunda static int wled_configure_ovp_irq(struct wled *wled,
15878663c188SKiran Gunda 				  struct platform_device *pdev)
15888663c188SKiran Gunda {
15898663c188SKiran Gunda 	int rc;
15908663c188SKiran Gunda 	u32 val;
15918663c188SKiran Gunda 
15928663c188SKiran Gunda 	wled->ovp_irq = platform_get_irq_byname(pdev, "ovp");
15938663c188SKiran Gunda 	if (wled->ovp_irq < 0) {
15948663c188SKiran Gunda 		dev_dbg(&pdev->dev, "OVP IRQ not found - disabling automatic string detection\n");
15958663c188SKiran Gunda 		return 0;
15968663c188SKiran Gunda 	}
15978663c188SKiran Gunda 
15988663c188SKiran Gunda 	rc = devm_request_threaded_irq(wled->dev, wled->ovp_irq, NULL,
15998663c188SKiran Gunda 				       wled_ovp_irq_handler, IRQF_ONESHOT,
16008663c188SKiran Gunda 				       "wled_ovp_irq", wled);
16018663c188SKiran Gunda 	if (rc < 0) {
16028663c188SKiran Gunda 		dev_err(wled->dev, "Unable to request ovp_irq (err:%d)\n",
16038663c188SKiran Gunda 			rc);
16048663c188SKiran Gunda 		wled->ovp_irq = 0;
16058663c188SKiran Gunda 		return 0;
16068663c188SKiran Gunda 	}
16078663c188SKiran Gunda 
16088663c188SKiran Gunda 	rc = regmap_read(wled->regmap, wled->ctrl_addr +
16098663c188SKiran Gunda 			 WLED3_CTRL_REG_MOD_EN, &val);
16108663c188SKiran Gunda 	if (rc < 0)
16118663c188SKiran Gunda 		return rc;
16128663c188SKiran Gunda 
16138663c188SKiran Gunda 	/* Keep OVP irq disabled until module is enabled */
16148663c188SKiran Gunda 	if (!(val & WLED3_CTRL_REG_MOD_EN_MASK))
16158663c188SKiran Gunda 		disable_irq(wled->ovp_irq);
16168663c188SKiran Gunda 
16178663c188SKiran Gunda 	return 0;
16188663c188SKiran Gunda }
16198663c188SKiran Gunda 
1620bb800a37SKiran Gunda static const struct backlight_ops wled_ops = {
1621bb800a37SKiran Gunda 	.update_status = wled_update_status,
16227ddbc242SBjorn Andersson };
16237ddbc242SBjorn Andersson 
1624bb800a37SKiran Gunda static int wled_probe(struct platform_device *pdev)
162593c64f1eSCourtney Cavin {
16267ddbc242SBjorn Andersson 	struct backlight_properties props;
16277ddbc242SBjorn Andersson 	struct backlight_device *bl;
1628bb800a37SKiran Gunda 	struct wled *wled;
162993c64f1eSCourtney Cavin 	struct regmap *regmap;
16309d6c2435SBjorn Andersson 	u32 val;
163193c64f1eSCourtney Cavin 	int rc;
163293c64f1eSCourtney Cavin 
163393c64f1eSCourtney Cavin 	regmap = dev_get_regmap(pdev->dev.parent, NULL);
163493c64f1eSCourtney Cavin 	if (!regmap) {
163593c64f1eSCourtney Cavin 		dev_err(&pdev->dev, "Unable to get regmap\n");
163693c64f1eSCourtney Cavin 		return -EINVAL;
163793c64f1eSCourtney Cavin 	}
163893c64f1eSCourtney Cavin 
163993c64f1eSCourtney Cavin 	wled = devm_kzalloc(&pdev->dev, sizeof(*wled), GFP_KERNEL);
164093c64f1eSCourtney Cavin 	if (!wled)
164193c64f1eSCourtney Cavin 		return -ENOMEM;
164293c64f1eSCourtney Cavin 
164393c64f1eSCourtney Cavin 	wled->regmap = regmap;
1644775d2ffbSKiran Gunda 	wled->dev = &pdev->dev;
164593c64f1eSCourtney Cavin 
1646f16899a6SKiran Gunda 	wled->version = (uintptr_t)of_device_get_match_data(&pdev->dev);
1647f16899a6SKiran Gunda 	if (!wled->version) {
1648775d2ffbSKiran Gunda 		dev_err(&pdev->dev, "Unknown device version\n");
1649775d2ffbSKiran Gunda 		return -ENODEV;
1650775d2ffbSKiran Gunda 	}
1651775d2ffbSKiran Gunda 
1652feeab87bSKiran Gunda 	mutex_init(&wled->lock);
1653f16899a6SKiran Gunda 	rc = wled_configure(wled);
165493c64f1eSCourtney Cavin 	if (rc)
165593c64f1eSCourtney Cavin 		return rc;
165693c64f1eSCourtney Cavin 
165762a1d3f6SSubbaraman Narayanamurthy 	val = WLED3_SINK_REG_BRIGHT_MAX;
165862a1d3f6SSubbaraman Narayanamurthy 	of_property_read_u32(pdev->dev.of_node, "max-brightness", &val);
165962a1d3f6SSubbaraman Narayanamurthy 	wled->max_brightness = val;
166062a1d3f6SSubbaraman Narayanamurthy 
1661f16899a6SKiran Gunda 	switch (wled->version) {
1662775d2ffbSKiran Gunda 	case 3:
16638663c188SKiran Gunda 		wled->cfg.auto_detection_enabled = false;
1664775d2ffbSKiran Gunda 		rc = wled3_setup(wled);
1665775d2ffbSKiran Gunda 		if (rc) {
1666775d2ffbSKiran Gunda 			dev_err(&pdev->dev, "wled3_setup failed\n");
166793c64f1eSCourtney Cavin 			return rc;
1668775d2ffbSKiran Gunda 		}
1669775d2ffbSKiran Gunda 		break;
1670775d2ffbSKiran Gunda 
167103b2b5e8SKiran Gunda 	case 4:
1672feeab87bSKiran Gunda 		wled->has_short_detect = true;
167303b2b5e8SKiran Gunda 		rc = wled4_setup(wled);
167403b2b5e8SKiran Gunda 		if (rc) {
167503b2b5e8SKiran Gunda 			dev_err(&pdev->dev, "wled4_setup failed\n");
167603b2b5e8SKiran Gunda 			return rc;
167703b2b5e8SKiran Gunda 		}
167803b2b5e8SKiran Gunda 		break;
167903b2b5e8SKiran Gunda 
168062a1d3f6SSubbaraman Narayanamurthy 	case 5:
168162a1d3f6SSubbaraman Narayanamurthy 		wled->has_short_detect = true;
168262a1d3f6SSubbaraman Narayanamurthy 		if (wled->cfg.cabc_sel)
168362a1d3f6SSubbaraman Narayanamurthy 			wled->max_brightness = WLED5_SINK_REG_BRIGHT_MAX_12B;
168462a1d3f6SSubbaraman Narayanamurthy 
168562a1d3f6SSubbaraman Narayanamurthy 		rc = wled5_setup(wled);
168662a1d3f6SSubbaraman Narayanamurthy 		if (rc) {
168762a1d3f6SSubbaraman Narayanamurthy 			dev_err(&pdev->dev, "wled5_setup failed\n");
168862a1d3f6SSubbaraman Narayanamurthy 			return rc;
168962a1d3f6SSubbaraman Narayanamurthy 		}
169062a1d3f6SSubbaraman Narayanamurthy 		break;
169162a1d3f6SSubbaraman Narayanamurthy 
1692775d2ffbSKiran Gunda 	default:
1693775d2ffbSKiran Gunda 		dev_err(wled->dev, "Invalid WLED version\n");
1694775d2ffbSKiran Gunda 		break;
1695775d2ffbSKiran Gunda 	}
169693c64f1eSCourtney Cavin 
16978663c188SKiran Gunda 	INIT_DELAYED_WORK(&wled->ovp_work, wled_ovp_work);
16988663c188SKiran Gunda 
1699feeab87bSKiran Gunda 	rc = wled_configure_short_irq(wled, pdev);
1700feeab87bSKiran Gunda 	if (rc < 0)
1701feeab87bSKiran Gunda 		return rc;
1702feeab87bSKiran Gunda 
17038663c188SKiran Gunda 	rc = wled_configure_ovp_irq(wled, pdev);
17048663c188SKiran Gunda 	if (rc < 0)
17058663c188SKiran Gunda 		return rc;
17068663c188SKiran Gunda 
1707bb800a37SKiran Gunda 	val = WLED_DEFAULT_BRIGHTNESS;
17089d6c2435SBjorn Andersson 	of_property_read_u32(pdev->dev.of_node, "default-brightness", &val);
17099d6c2435SBjorn Andersson 
17107ddbc242SBjorn Andersson 	memset(&props, 0, sizeof(struct backlight_properties));
17117ddbc242SBjorn Andersson 	props.type = BACKLIGHT_RAW;
17129d6c2435SBjorn Andersson 	props.brightness = val;
171362a1d3f6SSubbaraman Narayanamurthy 	props.max_brightness = wled->max_brightness;
17147ddbc242SBjorn Andersson 	bl = devm_backlight_device_register(&pdev->dev, wled->name,
17157ddbc242SBjorn Andersson 					    &pdev->dev, wled,
1716bb800a37SKiran Gunda 					    &wled_ops, &props);
1717fc18111bSkbuild test robot 	return PTR_ERR_OR_ZERO(bl);
171893c64f1eSCourtney Cavin };
171993c64f1eSCourtney Cavin 
1720feeab87bSKiran Gunda static int wled_remove(struct platform_device *pdev)
1721feeab87bSKiran Gunda {
17220b5e0f45SJulia Lawall 	struct wled *wled = platform_get_drvdata(pdev);
1723feeab87bSKiran Gunda 
1724feeab87bSKiran Gunda 	mutex_destroy(&wled->lock);
17258663c188SKiran Gunda 	cancel_delayed_work_sync(&wled->ovp_work);
1726feeab87bSKiran Gunda 	disable_irq(wled->short_irq);
17278663c188SKiran Gunda 	disable_irq(wled->ovp_irq);
1728feeab87bSKiran Gunda 
1729feeab87bSKiran Gunda 	return 0;
1730feeab87bSKiran Gunda }
1731feeab87bSKiran Gunda 
1732bb800a37SKiran Gunda static const struct of_device_id wled_match_table[] = {
1733775d2ffbSKiran Gunda 	{ .compatible = "qcom,pm8941-wled", .data = (void *)3 },
17346fc632d3SKonrad Dybcio 	{ .compatible = "qcom,pmi8994-wled", .data = (void *)4 },
173503b2b5e8SKiran Gunda 	{ .compatible = "qcom,pmi8998-wled", .data = (void *)4 },
173603b2b5e8SKiran Gunda 	{ .compatible = "qcom,pm660l-wled", .data = (void *)4 },
1737*fe4059c3SLuca Weiss 	{ .compatible = "qcom,pm6150l-wled", .data = (void *)5 },
173862a1d3f6SSubbaraman Narayanamurthy 	{ .compatible = "qcom,pm8150l-wled", .data = (void *)5 },
173993c64f1eSCourtney Cavin 	{}
174093c64f1eSCourtney Cavin };
1741bb800a37SKiran Gunda MODULE_DEVICE_TABLE(of, wled_match_table);
174293c64f1eSCourtney Cavin 
1743bb800a37SKiran Gunda static struct platform_driver wled_driver = {
1744bb800a37SKiran Gunda 	.probe = wled_probe,
1745feeab87bSKiran Gunda 	.remove = wled_remove,
174693c64f1eSCourtney Cavin 	.driver	= {
1747bb800a37SKiran Gunda 		.name = "qcom,wled",
1748bb800a37SKiran Gunda 		.of_match_table	= wled_match_table,
174993c64f1eSCourtney Cavin 	},
175093c64f1eSCourtney Cavin };
175193c64f1eSCourtney Cavin 
1752bb800a37SKiran Gunda module_platform_driver(wled_driver);
175393c64f1eSCourtney Cavin 
1754bb800a37SKiran Gunda MODULE_DESCRIPTION("Qualcomm WLED driver");
175593c64f1eSCourtney Cavin MODULE_LICENSE("GPL v2");
1756