18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/* Copyright (c) 2015, Sony Mobile Communications, AB.
38c2ecf20Sopenharmony_ci */
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <linux/delay.h>
68c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
78c2ecf20Sopenharmony_ci#include <linux/ktime.h>
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/backlight.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/of.h>
128c2ecf20Sopenharmony_ci#include <linux/of_device.h>
138c2ecf20Sopenharmony_ci#include <linux/of_address.h>
148c2ecf20Sopenharmony_ci#include <linux/regmap.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/* From DT binding */
178c2ecf20Sopenharmony_ci#define WLED_MAX_STRINGS				4
188c2ecf20Sopenharmony_ci#define MOD_A						0
198c2ecf20Sopenharmony_ci#define MOD_B						1
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define WLED_DEFAULT_BRIGHTNESS				2048
228c2ecf20Sopenharmony_ci#define WLED_SOFT_START_DLY_US				10000
238c2ecf20Sopenharmony_ci#define WLED3_SINK_REG_BRIGHT_MAX			0xFFF
248c2ecf20Sopenharmony_ci#define WLED5_SINK_REG_BRIGHT_MAX_12B			0xFFF
258c2ecf20Sopenharmony_ci#define WLED5_SINK_REG_BRIGHT_MAX_15B			0x7FFF
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* WLED3/WLED4 control registers */
288c2ecf20Sopenharmony_ci#define WLED3_CTRL_REG_FAULT_STATUS			0x08
298c2ecf20Sopenharmony_ci#define  WLED3_CTRL_REG_ILIM_FAULT_BIT			BIT(0)
308c2ecf20Sopenharmony_ci#define  WLED3_CTRL_REG_OVP_FAULT_BIT			BIT(1)
318c2ecf20Sopenharmony_ci#define  WLED4_CTRL_REG_SC_FAULT_BIT			BIT(2)
328c2ecf20Sopenharmony_ci#define  WLED5_CTRL_REG_OVP_PRE_ALARM_BIT		BIT(4)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define WLED3_CTRL_REG_INT_RT_STS			0x10
358c2ecf20Sopenharmony_ci#define  WLED3_CTRL_REG_OVP_FAULT_STATUS		BIT(1)
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define WLED3_CTRL_REG_MOD_EN				0x46
388c2ecf20Sopenharmony_ci#define  WLED3_CTRL_REG_MOD_EN_MASK			BIT(7)
398c2ecf20Sopenharmony_ci#define  WLED3_CTRL_REG_MOD_EN_SHIFT			7
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define WLED3_CTRL_REG_FEEDBACK_CONTROL			0x48
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define WLED3_CTRL_REG_FREQ				0x4c
448c2ecf20Sopenharmony_ci#define  WLED3_CTRL_REG_FREQ_MASK			GENMASK(3, 0)
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define WLED3_CTRL_REG_OVP				0x4d
478c2ecf20Sopenharmony_ci#define  WLED3_CTRL_REG_OVP_MASK			GENMASK(1, 0)
488c2ecf20Sopenharmony_ci#define  WLED5_CTRL_REG_OVP_MASK			GENMASK(3, 0)
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#define WLED3_CTRL_REG_ILIMIT				0x4e
518c2ecf20Sopenharmony_ci#define  WLED3_CTRL_REG_ILIMIT_MASK			GENMASK(2, 0)
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/* WLED3/WLED4 sink registers */
548c2ecf20Sopenharmony_ci#define WLED3_SINK_REG_SYNC				0x47
558c2ecf20Sopenharmony_ci#define  WLED3_SINK_REG_SYNC_CLEAR			0x00
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#define WLED3_SINK_REG_CURR_SINK			0x4f
588c2ecf20Sopenharmony_ci#define  WLED3_SINK_REG_CURR_SINK_MASK			GENMASK(7, 5)
598c2ecf20Sopenharmony_ci#define  WLED3_SINK_REG_CURR_SINK_SHFT			5
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/* WLED3 specific per-'string' registers below */
628c2ecf20Sopenharmony_ci#define WLED3_SINK_REG_BRIGHT(n)			(0x40 + n)
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#define WLED3_SINK_REG_STR_MOD_EN(n)			(0x60 + (n * 0x10))
658c2ecf20Sopenharmony_ci#define  WLED3_SINK_REG_STR_MOD_MASK			BIT(7)
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#define WLED3_SINK_REG_STR_FULL_SCALE_CURR(n)		(0x62 + (n * 0x10))
688c2ecf20Sopenharmony_ci#define  WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK	GENMASK(4, 0)
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#define WLED3_SINK_REG_STR_MOD_SRC(n)			(0x63 + (n * 0x10))
718c2ecf20Sopenharmony_ci#define  WLED3_SINK_REG_STR_MOD_SRC_MASK		BIT(0)
728c2ecf20Sopenharmony_ci#define  WLED3_SINK_REG_STR_MOD_SRC_INT			0x00
738c2ecf20Sopenharmony_ci#define  WLED3_SINK_REG_STR_MOD_SRC_EXT			0x01
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#define WLED3_SINK_REG_STR_CABC(n)			(0x66 + (n * 0x10))
768c2ecf20Sopenharmony_ci#define  WLED3_SINK_REG_STR_CABC_MASK			BIT(7)
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/* WLED4 specific control registers */
798c2ecf20Sopenharmony_ci#define WLED4_CTRL_REG_SHORT_PROTECT			0x5e
808c2ecf20Sopenharmony_ci#define  WLED4_CTRL_REG_SHORT_EN_MASK			BIT(7)
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#define WLED4_CTRL_REG_SEC_ACCESS			0xd0
838c2ecf20Sopenharmony_ci#define  WLED4_CTRL_REG_SEC_UNLOCK			0xa5
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci#define WLED4_CTRL_REG_TEST1				0xe2
868c2ecf20Sopenharmony_ci#define  WLED4_CTRL_REG_TEST1_EXT_FET_DTEST2		0x09
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci/* WLED4 specific sink registers */
898c2ecf20Sopenharmony_ci#define WLED4_SINK_REG_CURR_SINK			0x46
908c2ecf20Sopenharmony_ci#define  WLED4_SINK_REG_CURR_SINK_MASK			GENMASK(7, 4)
918c2ecf20Sopenharmony_ci#define  WLED4_SINK_REG_CURR_SINK_SHFT			4
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/* WLED4 specific per-'string' registers below */
948c2ecf20Sopenharmony_ci#define WLED4_SINK_REG_STR_MOD_EN(n)			(0x50 + (n * 0x10))
958c2ecf20Sopenharmony_ci#define  WLED4_SINK_REG_STR_MOD_MASK			BIT(7)
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci#define WLED4_SINK_REG_STR_FULL_SCALE_CURR(n)		(0x52 + (n * 0x10))
988c2ecf20Sopenharmony_ci#define  WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK	GENMASK(3, 0)
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci#define WLED4_SINK_REG_STR_MOD_SRC(n)			(0x53 + (n * 0x10))
1018c2ecf20Sopenharmony_ci#define  WLED4_SINK_REG_STR_MOD_SRC_MASK		BIT(0)
1028c2ecf20Sopenharmony_ci#define  WLED4_SINK_REG_STR_MOD_SRC_INT			0x00
1038c2ecf20Sopenharmony_ci#define  WLED4_SINK_REG_STR_MOD_SRC_EXT			0x01
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci#define WLED4_SINK_REG_STR_CABC(n)			(0x56 + (n * 0x10))
1068c2ecf20Sopenharmony_ci#define  WLED4_SINK_REG_STR_CABC_MASK			BIT(7)
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci#define WLED4_SINK_REG_BRIGHT(n)			(0x57 + (n * 0x10))
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci/* WLED5 specific control registers */
1118c2ecf20Sopenharmony_ci#define WLED5_CTRL_REG_OVP_INT_CTL			0x5f
1128c2ecf20Sopenharmony_ci#define  WLED5_CTRL_REG_OVP_INT_TIMER_MASK		GENMASK(2, 0)
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/* WLED5 specific sink registers */
1158c2ecf20Sopenharmony_ci#define WLED5_SINK_REG_MOD_A_EN				0x50
1168c2ecf20Sopenharmony_ci#define WLED5_SINK_REG_MOD_B_EN				0x60
1178c2ecf20Sopenharmony_ci#define  WLED5_SINK_REG_MOD_EN_MASK			BIT(7)
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci#define WLED5_SINK_REG_MOD_A_SRC_SEL			0x51
1208c2ecf20Sopenharmony_ci#define WLED5_SINK_REG_MOD_B_SRC_SEL			0x61
1218c2ecf20Sopenharmony_ci#define  WLED5_SINK_REG_MOD_SRC_SEL_HIGH		0
1228c2ecf20Sopenharmony_ci#define  WLED5_SINK_REG_MOD_SRC_SEL_EXT			0x03
1238c2ecf20Sopenharmony_ci#define  WLED5_SINK_REG_MOD_SRC_SEL_MASK		GENMASK(1, 0)
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci#define WLED5_SINK_REG_MOD_A_BRIGHTNESS_WIDTH_SEL	0x52
1268c2ecf20Sopenharmony_ci#define WLED5_SINK_REG_MOD_B_BRIGHTNESS_WIDTH_SEL	0x62
1278c2ecf20Sopenharmony_ci#define  WLED5_SINK_REG_BRIGHTNESS_WIDTH_12B		0
1288c2ecf20Sopenharmony_ci#define  WLED5_SINK_REG_BRIGHTNESS_WIDTH_15B		1
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci#define WLED5_SINK_REG_MOD_A_BRIGHTNESS_LSB		0x53
1318c2ecf20Sopenharmony_ci#define WLED5_SINK_REG_MOD_A_BRIGHTNESS_MSB		0x54
1328c2ecf20Sopenharmony_ci#define WLED5_SINK_REG_MOD_B_BRIGHTNESS_LSB		0x63
1338c2ecf20Sopenharmony_ci#define WLED5_SINK_REG_MOD_B_BRIGHTNESS_MSB		0x64
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci#define WLED5_SINK_REG_MOD_SYNC_BIT			0x65
1368c2ecf20Sopenharmony_ci#define  WLED5_SINK_REG_SYNC_MOD_A_BIT			BIT(0)
1378c2ecf20Sopenharmony_ci#define  WLED5_SINK_REG_SYNC_MOD_B_BIT			BIT(1)
1388c2ecf20Sopenharmony_ci#define  WLED5_SINK_REG_SYNC_MASK			GENMASK(1, 0)
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/* WLED5 specific per-'string' registers below */
1418c2ecf20Sopenharmony_ci#define WLED5_SINK_REG_STR_FULL_SCALE_CURR(n)		(0x72 + (n * 0x10))
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci#define WLED5_SINK_REG_STR_SRC_SEL(n)			(0x73 + (n * 0x10))
1448c2ecf20Sopenharmony_ci#define  WLED5_SINK_REG_SRC_SEL_MOD_A			0
1458c2ecf20Sopenharmony_ci#define  WLED5_SINK_REG_SRC_SEL_MOD_B			1
1468c2ecf20Sopenharmony_ci#define  WLED5_SINK_REG_SRC_SEL_MASK			GENMASK(1, 0)
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistruct wled_var_cfg {
1498c2ecf20Sopenharmony_ci	const u32 *values;
1508c2ecf20Sopenharmony_ci	u32 (*fn)(u32);
1518c2ecf20Sopenharmony_ci	int size;
1528c2ecf20Sopenharmony_ci};
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistruct wled_u32_opts {
1558c2ecf20Sopenharmony_ci	const char *name;
1568c2ecf20Sopenharmony_ci	u32 *val_ptr;
1578c2ecf20Sopenharmony_ci	const struct wled_var_cfg *cfg;
1588c2ecf20Sopenharmony_ci};
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistruct wled_bool_opts {
1618c2ecf20Sopenharmony_ci	const char *name;
1628c2ecf20Sopenharmony_ci	bool *val_ptr;
1638c2ecf20Sopenharmony_ci};
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistruct wled_config {
1668c2ecf20Sopenharmony_ci	u32 boost_i_limit;
1678c2ecf20Sopenharmony_ci	u32 ovp;
1688c2ecf20Sopenharmony_ci	u32 switch_freq;
1698c2ecf20Sopenharmony_ci	u32 num_strings;
1708c2ecf20Sopenharmony_ci	u32 string_i_limit;
1718c2ecf20Sopenharmony_ci	u32 enabled_strings[WLED_MAX_STRINGS];
1728c2ecf20Sopenharmony_ci	u32 mod_sel;
1738c2ecf20Sopenharmony_ci	u32 cabc_sel;
1748c2ecf20Sopenharmony_ci	bool cs_out_en;
1758c2ecf20Sopenharmony_ci	bool ext_gen;
1768c2ecf20Sopenharmony_ci	bool cabc;
1778c2ecf20Sopenharmony_ci	bool external_pfet;
1788c2ecf20Sopenharmony_ci	bool auto_detection_enabled;
1798c2ecf20Sopenharmony_ci};
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistruct wled {
1828c2ecf20Sopenharmony_ci	const char *name;
1838c2ecf20Sopenharmony_ci	struct device *dev;
1848c2ecf20Sopenharmony_ci	struct regmap *regmap;
1858c2ecf20Sopenharmony_ci	struct mutex lock;	/* Lock to avoid race from thread irq handler */
1868c2ecf20Sopenharmony_ci	ktime_t last_short_event;
1878c2ecf20Sopenharmony_ci	ktime_t start_ovp_fault_time;
1888c2ecf20Sopenharmony_ci	u16 ctrl_addr;
1898c2ecf20Sopenharmony_ci	u16 sink_addr;
1908c2ecf20Sopenharmony_ci	u16 max_string_count;
1918c2ecf20Sopenharmony_ci	u16 auto_detection_ovp_count;
1928c2ecf20Sopenharmony_ci	u32 brightness;
1938c2ecf20Sopenharmony_ci	u32 max_brightness;
1948c2ecf20Sopenharmony_ci	u32 short_count;
1958c2ecf20Sopenharmony_ci	u32 auto_detect_count;
1968c2ecf20Sopenharmony_ci	u32 version;
1978c2ecf20Sopenharmony_ci	bool disabled_by_short;
1988c2ecf20Sopenharmony_ci	bool has_short_detect;
1998c2ecf20Sopenharmony_ci	bool cabc_disabled;
2008c2ecf20Sopenharmony_ci	int short_irq;
2018c2ecf20Sopenharmony_ci	int ovp_irq;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	struct wled_config cfg;
2048c2ecf20Sopenharmony_ci	struct delayed_work ovp_work;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/* Configures the brightness. Applicable for wled3, wled4 and wled5 */
2078c2ecf20Sopenharmony_ci	int (*wled_set_brightness)(struct wled *wled, u16 brightness);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/* Configures the cabc register. Applicable for wled4 and wled5 */
2108c2ecf20Sopenharmony_ci	int (*wled_cabc_config)(struct wled *wled, bool enable);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/*
2138c2ecf20Sopenharmony_ci	 * Toggles the sync bit for the brightness update to take place.
2148c2ecf20Sopenharmony_ci	 * Applicable for WLED3, WLED4 and WLED5.
2158c2ecf20Sopenharmony_ci	 */
2168c2ecf20Sopenharmony_ci	int (*wled_sync_toggle)(struct wled *wled);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	/*
2198c2ecf20Sopenharmony_ci	 * Time to wait before checking the OVP status after wled module enable.
2208c2ecf20Sopenharmony_ci	 * Applicable for WLED4 and WLED5.
2218c2ecf20Sopenharmony_ci	 */
2228c2ecf20Sopenharmony_ci	int (*wled_ovp_delay)(struct wled *wled);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/*
2258c2ecf20Sopenharmony_ci	 * Determines if the auto string detection is required.
2268c2ecf20Sopenharmony_ci	 * Applicable for WLED4 and WLED5
2278c2ecf20Sopenharmony_ci	 */
2288c2ecf20Sopenharmony_ci	bool (*wled_auto_detection_required)(struct wled *wled);
2298c2ecf20Sopenharmony_ci};
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic int wled3_set_brightness(struct wled *wled, u16 brightness)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	int rc, i;
2348c2ecf20Sopenharmony_ci	__le16 v;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	v = cpu_to_le16(brightness & WLED3_SINK_REG_BRIGHT_MAX);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	for (i = 0;  i < wled->cfg.num_strings; ++i) {
2398c2ecf20Sopenharmony_ci		rc = regmap_bulk_write(wled->regmap, wled->ctrl_addr +
2408c2ecf20Sopenharmony_ci				       WLED3_SINK_REG_BRIGHT(wled->cfg.enabled_strings[i]),
2418c2ecf20Sopenharmony_ci				       &v, sizeof(v));
2428c2ecf20Sopenharmony_ci		if (rc < 0)
2438c2ecf20Sopenharmony_ci			return rc;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	return 0;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic int wled4_set_brightness(struct wled *wled, u16 brightness)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	int rc, i;
2528c2ecf20Sopenharmony_ci	u16 low_limit = wled->max_brightness * 4 / 1000;
2538c2ecf20Sopenharmony_ci	__le16 v;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	/* WLED4's lower limit of operation is 0.4% */
2568c2ecf20Sopenharmony_ci	if (brightness > 0 && brightness < low_limit)
2578c2ecf20Sopenharmony_ci		brightness = low_limit;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	v = cpu_to_le16(brightness & WLED3_SINK_REG_BRIGHT_MAX);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	for (i = 0;  i < wled->cfg.num_strings; ++i) {
2628c2ecf20Sopenharmony_ci		rc = regmap_bulk_write(wled->regmap, wled->sink_addr +
2638c2ecf20Sopenharmony_ci				       WLED4_SINK_REG_BRIGHT(wled->cfg.enabled_strings[i]),
2648c2ecf20Sopenharmony_ci				       &v, sizeof(v));
2658c2ecf20Sopenharmony_ci		if (rc < 0)
2668c2ecf20Sopenharmony_ci			return rc;
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	return 0;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic int wled5_set_brightness(struct wled *wled, u16 brightness)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	int rc, offset;
2758c2ecf20Sopenharmony_ci	u16 low_limit = wled->max_brightness * 1 / 1000;
2768c2ecf20Sopenharmony_ci	__le16 v;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	/* WLED5's lower limit is 0.1% */
2798c2ecf20Sopenharmony_ci	if (brightness < low_limit)
2808c2ecf20Sopenharmony_ci		brightness = low_limit;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	v = cpu_to_le16(brightness & WLED5_SINK_REG_BRIGHT_MAX_15B);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	offset = (wled->cfg.mod_sel == MOD_A) ?
2858c2ecf20Sopenharmony_ci		  WLED5_SINK_REG_MOD_A_BRIGHTNESS_LSB :
2868c2ecf20Sopenharmony_ci		  WLED5_SINK_REG_MOD_B_BRIGHTNESS_LSB;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	rc = regmap_bulk_write(wled->regmap, wled->sink_addr + offset,
2898c2ecf20Sopenharmony_ci			       &v, sizeof(v));
2908c2ecf20Sopenharmony_ci	return rc;
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic void wled_ovp_work(struct work_struct *work)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	struct wled *wled = container_of(work,
2968c2ecf20Sopenharmony_ci					 struct wled, ovp_work.work);
2978c2ecf20Sopenharmony_ci	enable_irq(wled->ovp_irq);
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic int wled_module_enable(struct wled *wled, int val)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	int rc;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (wled->disabled_by_short)
3058c2ecf20Sopenharmony_ci		return -ENXIO;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
3088c2ecf20Sopenharmony_ci				WLED3_CTRL_REG_MOD_EN,
3098c2ecf20Sopenharmony_ci				WLED3_CTRL_REG_MOD_EN_MASK,
3108c2ecf20Sopenharmony_ci				val << WLED3_CTRL_REG_MOD_EN_SHIFT);
3118c2ecf20Sopenharmony_ci	if (rc < 0)
3128c2ecf20Sopenharmony_ci		return rc;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (wled->ovp_irq > 0) {
3158c2ecf20Sopenharmony_ci		if (val) {
3168c2ecf20Sopenharmony_ci			/*
3178c2ecf20Sopenharmony_ci			 * The hardware generates a storm of spurious OVP
3188c2ecf20Sopenharmony_ci			 * interrupts during soft start operations. So defer
3198c2ecf20Sopenharmony_ci			 * enabling the IRQ for 10ms to ensure that the
3208c2ecf20Sopenharmony_ci			 * soft start is complete.
3218c2ecf20Sopenharmony_ci			 */
3228c2ecf20Sopenharmony_ci			schedule_delayed_work(&wled->ovp_work, HZ / 100);
3238c2ecf20Sopenharmony_ci		} else {
3248c2ecf20Sopenharmony_ci			if (!cancel_delayed_work_sync(&wled->ovp_work))
3258c2ecf20Sopenharmony_ci				disable_irq(wled->ovp_irq);
3268c2ecf20Sopenharmony_ci		}
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	return 0;
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic int wled3_sync_toggle(struct wled *wled)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	int rc;
3358c2ecf20Sopenharmony_ci	unsigned int mask = GENMASK(wled->max_string_count - 1, 0);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap,
3388c2ecf20Sopenharmony_ci				wled->sink_addr + WLED3_SINK_REG_SYNC,
3398c2ecf20Sopenharmony_ci				mask, mask);
3408c2ecf20Sopenharmony_ci	if (rc < 0)
3418c2ecf20Sopenharmony_ci		return rc;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap,
3448c2ecf20Sopenharmony_ci				wled->sink_addr + WLED3_SINK_REG_SYNC,
3458c2ecf20Sopenharmony_ci				mask, WLED3_SINK_REG_SYNC_CLEAR);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	return rc;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic int wled5_mod_sync_toggle(struct wled *wled)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	int rc;
3538c2ecf20Sopenharmony_ci	u8 val;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	val = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_REG_SYNC_MOD_A_BIT :
3568c2ecf20Sopenharmony_ci					     WLED5_SINK_REG_SYNC_MOD_B_BIT;
3578c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap,
3588c2ecf20Sopenharmony_ci				wled->sink_addr + WLED5_SINK_REG_MOD_SYNC_BIT,
3598c2ecf20Sopenharmony_ci				WLED5_SINK_REG_SYNC_MASK, val);
3608c2ecf20Sopenharmony_ci	if (rc < 0)
3618c2ecf20Sopenharmony_ci		return rc;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	return regmap_update_bits(wled->regmap,
3648c2ecf20Sopenharmony_ci				  wled->sink_addr + WLED5_SINK_REG_MOD_SYNC_BIT,
3658c2ecf20Sopenharmony_ci				  WLED5_SINK_REG_SYNC_MASK, 0);
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic int wled_ovp_fault_status(struct wled *wled, bool *fault_set)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	int rc;
3718c2ecf20Sopenharmony_ci	u32 int_rt_sts, fault_sts;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	*fault_set = false;
3748c2ecf20Sopenharmony_ci	rc = regmap_read(wled->regmap,
3758c2ecf20Sopenharmony_ci			wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS,
3768c2ecf20Sopenharmony_ci			&int_rt_sts);
3778c2ecf20Sopenharmony_ci	if (rc < 0) {
3788c2ecf20Sopenharmony_ci		dev_err(wled->dev, "Failed to read INT_RT_STS rc=%d\n", rc);
3798c2ecf20Sopenharmony_ci		return rc;
3808c2ecf20Sopenharmony_ci	}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	rc = regmap_read(wled->regmap,
3838c2ecf20Sopenharmony_ci			wled->ctrl_addr + WLED3_CTRL_REG_FAULT_STATUS,
3848c2ecf20Sopenharmony_ci			&fault_sts);
3858c2ecf20Sopenharmony_ci	if (rc < 0) {
3868c2ecf20Sopenharmony_ci		dev_err(wled->dev, "Failed to read FAULT_STATUS rc=%d\n", rc);
3878c2ecf20Sopenharmony_ci		return rc;
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	if (int_rt_sts & WLED3_CTRL_REG_OVP_FAULT_STATUS)
3918c2ecf20Sopenharmony_ci		*fault_set = true;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	if (wled->version == 4 && (fault_sts & WLED3_CTRL_REG_OVP_FAULT_BIT))
3948c2ecf20Sopenharmony_ci		*fault_set = true;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	if (wled->version == 5 && (fault_sts & (WLED3_CTRL_REG_OVP_FAULT_BIT |
3978c2ecf20Sopenharmony_ci				   WLED5_CTRL_REG_OVP_PRE_ALARM_BIT)))
3988c2ecf20Sopenharmony_ci		*fault_set = true;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	if (*fault_set)
4018c2ecf20Sopenharmony_ci		dev_dbg(wled->dev, "WLED OVP fault detected, int_rt_sts=0x%x fault_sts=0x%x\n",
4028c2ecf20Sopenharmony_ci			int_rt_sts, fault_sts);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	return rc;
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic int wled4_ovp_delay(struct wled *wled)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	return WLED_SOFT_START_DLY_US;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic int wled5_ovp_delay(struct wled *wled)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	int rc, delay_us;
4158c2ecf20Sopenharmony_ci	u32 val;
4168c2ecf20Sopenharmony_ci	u8 ovp_timer_ms[8] = {1, 2, 4, 8, 12, 16, 20, 24};
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	/* For WLED5, get the delay based on OVP timer */
4198c2ecf20Sopenharmony_ci	rc = regmap_read(wled->regmap, wled->ctrl_addr +
4208c2ecf20Sopenharmony_ci			 WLED5_CTRL_REG_OVP_INT_CTL, &val);
4218c2ecf20Sopenharmony_ci	if (rc < 0)
4228c2ecf20Sopenharmony_ci		delay_us =
4238c2ecf20Sopenharmony_ci		ovp_timer_ms[val & WLED5_CTRL_REG_OVP_INT_TIMER_MASK] * 1000;
4248c2ecf20Sopenharmony_ci	else
4258c2ecf20Sopenharmony_ci		delay_us = 2 * WLED_SOFT_START_DLY_US;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	dev_dbg(wled->dev, "delay_time_us: %d\n", delay_us);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	return delay_us;
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_cistatic int wled_update_status(struct backlight_device *bl)
4338c2ecf20Sopenharmony_ci{
4348c2ecf20Sopenharmony_ci	struct wled *wled = bl_get_data(bl);
4358c2ecf20Sopenharmony_ci	u16 brightness = backlight_get_brightness(bl);
4368c2ecf20Sopenharmony_ci	int rc = 0;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	mutex_lock(&wled->lock);
4398c2ecf20Sopenharmony_ci	if (brightness) {
4408c2ecf20Sopenharmony_ci		rc = wled->wled_set_brightness(wled, brightness);
4418c2ecf20Sopenharmony_ci		if (rc < 0) {
4428c2ecf20Sopenharmony_ci			dev_err(wled->dev, "wled failed to set brightness rc:%d\n",
4438c2ecf20Sopenharmony_ci				rc);
4448c2ecf20Sopenharmony_ci			goto unlock_mutex;
4458c2ecf20Sopenharmony_ci		}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci		if (wled->version < 5) {
4488c2ecf20Sopenharmony_ci			rc = wled->wled_sync_toggle(wled);
4498c2ecf20Sopenharmony_ci			if (rc < 0) {
4508c2ecf20Sopenharmony_ci				dev_err(wled->dev, "wled sync failed rc:%d\n", rc);
4518c2ecf20Sopenharmony_ci				goto unlock_mutex;
4528c2ecf20Sopenharmony_ci			}
4538c2ecf20Sopenharmony_ci		} else {
4548c2ecf20Sopenharmony_ci			/*
4558c2ecf20Sopenharmony_ci			 * For WLED5 toggling the MOD_SYNC_BIT updates the
4568c2ecf20Sopenharmony_ci			 * brightness
4578c2ecf20Sopenharmony_ci			 */
4588c2ecf20Sopenharmony_ci			rc = wled5_mod_sync_toggle(wled);
4598c2ecf20Sopenharmony_ci			if (rc < 0) {
4608c2ecf20Sopenharmony_ci				dev_err(wled->dev, "wled mod sync failed rc:%d\n",
4618c2ecf20Sopenharmony_ci					rc);
4628c2ecf20Sopenharmony_ci				goto unlock_mutex;
4638c2ecf20Sopenharmony_ci			}
4648c2ecf20Sopenharmony_ci		}
4658c2ecf20Sopenharmony_ci	}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	if (!!brightness != !!wled->brightness) {
4688c2ecf20Sopenharmony_ci		rc = wled_module_enable(wled, !!brightness);
4698c2ecf20Sopenharmony_ci		if (rc < 0) {
4708c2ecf20Sopenharmony_ci			dev_err(wled->dev, "wled enable failed rc:%d\n", rc);
4718c2ecf20Sopenharmony_ci			goto unlock_mutex;
4728c2ecf20Sopenharmony_ci		}
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	wled->brightness = brightness;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ciunlock_mutex:
4788c2ecf20Sopenharmony_ci	mutex_unlock(&wled->lock);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	return rc;
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_cistatic int wled4_cabc_config(struct wled *wled, bool enable)
4848c2ecf20Sopenharmony_ci{
4858c2ecf20Sopenharmony_ci	int i, j, rc;
4868c2ecf20Sopenharmony_ci	u8 val;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	for (i = 0; i < wled->cfg.num_strings; i++) {
4898c2ecf20Sopenharmony_ci		j = wled->cfg.enabled_strings[i];
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci		val = enable ? WLED4_SINK_REG_STR_CABC_MASK : 0;
4928c2ecf20Sopenharmony_ci		rc = regmap_update_bits(wled->regmap, wled->sink_addr +
4938c2ecf20Sopenharmony_ci					WLED4_SINK_REG_STR_CABC(j),
4948c2ecf20Sopenharmony_ci					WLED4_SINK_REG_STR_CABC_MASK, val);
4958c2ecf20Sopenharmony_ci		if (rc < 0)
4968c2ecf20Sopenharmony_ci			return rc;
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	return 0;
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic int wled5_cabc_config(struct wled *wled, bool enable)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	int rc, offset;
5058c2ecf20Sopenharmony_ci	u8 reg;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	if (wled->cabc_disabled)
5088c2ecf20Sopenharmony_ci		return 0;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	reg = enable ? wled->cfg.cabc_sel : 0;
5118c2ecf20Sopenharmony_ci	offset = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_REG_MOD_A_SRC_SEL :
5128c2ecf20Sopenharmony_ci						WLED5_SINK_REG_MOD_B_SRC_SEL;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap, wled->sink_addr + offset,
5158c2ecf20Sopenharmony_ci				WLED5_SINK_REG_MOD_SRC_SEL_MASK, reg);
5168c2ecf20Sopenharmony_ci	if (rc < 0) {
5178c2ecf20Sopenharmony_ci		pr_err("Error in configuring CABC rc=%d\n", rc);
5188c2ecf20Sopenharmony_ci		return rc;
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (!wled->cfg.cabc_sel)
5228c2ecf20Sopenharmony_ci		wled->cabc_disabled = true;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	return 0;
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci#define WLED_SHORT_DLY_MS			20
5288c2ecf20Sopenharmony_ci#define WLED_SHORT_CNT_MAX			5
5298c2ecf20Sopenharmony_ci#define WLED_SHORT_RESET_CNT_DLY_US		USEC_PER_SEC
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_cistatic irqreturn_t wled_short_irq_handler(int irq, void *_wled)
5328c2ecf20Sopenharmony_ci{
5338c2ecf20Sopenharmony_ci	struct wled *wled = _wled;
5348c2ecf20Sopenharmony_ci	int rc;
5358c2ecf20Sopenharmony_ci	s64 elapsed_time;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	wled->short_count++;
5388c2ecf20Sopenharmony_ci	mutex_lock(&wled->lock);
5398c2ecf20Sopenharmony_ci	rc = wled_module_enable(wled, false);
5408c2ecf20Sopenharmony_ci	if (rc < 0) {
5418c2ecf20Sopenharmony_ci		dev_err(wled->dev, "wled disable failed rc:%d\n", rc);
5428c2ecf20Sopenharmony_ci		goto unlock_mutex;
5438c2ecf20Sopenharmony_ci	}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	elapsed_time = ktime_us_delta(ktime_get(),
5468c2ecf20Sopenharmony_ci				      wled->last_short_event);
5478c2ecf20Sopenharmony_ci	if (elapsed_time > WLED_SHORT_RESET_CNT_DLY_US)
5488c2ecf20Sopenharmony_ci		wled->short_count = 1;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	if (wled->short_count > WLED_SHORT_CNT_MAX) {
5518c2ecf20Sopenharmony_ci		dev_err(wled->dev, "Short triggered %d times, disabling WLED forever!\n",
5528c2ecf20Sopenharmony_ci			wled->short_count);
5538c2ecf20Sopenharmony_ci		wled->disabled_by_short = true;
5548c2ecf20Sopenharmony_ci		goto unlock_mutex;
5558c2ecf20Sopenharmony_ci	}
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	wled->last_short_event = ktime_get();
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	msleep(WLED_SHORT_DLY_MS);
5608c2ecf20Sopenharmony_ci	rc = wled_module_enable(wled, true);
5618c2ecf20Sopenharmony_ci	if (rc < 0)
5628c2ecf20Sopenharmony_ci		dev_err(wled->dev, "wled enable failed rc:%d\n", rc);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ciunlock_mutex:
5658c2ecf20Sopenharmony_ci	mutex_unlock(&wled->lock);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci#define AUTO_DETECT_BRIGHTNESS		200
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_cistatic void wled_auto_string_detection(struct wled *wled)
5738c2ecf20Sopenharmony_ci{
5748c2ecf20Sopenharmony_ci	int rc = 0, i, j, delay_time_us;
5758c2ecf20Sopenharmony_ci	u32 sink_config = 0;
5768c2ecf20Sopenharmony_ci	u8 sink_test = 0, sink_valid = 0, val;
5778c2ecf20Sopenharmony_ci	bool fault_set;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	/* Read configured sink configuration */
5808c2ecf20Sopenharmony_ci	rc = regmap_read(wled->regmap, wled->sink_addr +
5818c2ecf20Sopenharmony_ci			 WLED4_SINK_REG_CURR_SINK, &sink_config);
5828c2ecf20Sopenharmony_ci	if (rc < 0) {
5838c2ecf20Sopenharmony_ci		dev_err(wled->dev, "Failed to read SINK configuration rc=%d\n",
5848c2ecf20Sopenharmony_ci			rc);
5858c2ecf20Sopenharmony_ci		goto failed_detect;
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	/* Disable the module before starting detection */
5898c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap,
5908c2ecf20Sopenharmony_ci				wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN,
5918c2ecf20Sopenharmony_ci				WLED3_CTRL_REG_MOD_EN_MASK, 0);
5928c2ecf20Sopenharmony_ci	if (rc < 0) {
5938c2ecf20Sopenharmony_ci		dev_err(wled->dev, "Failed to disable WLED module rc=%d\n", rc);
5948c2ecf20Sopenharmony_ci		goto failed_detect;
5958c2ecf20Sopenharmony_ci	}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	/* Set low brightness across all sinks */
5988c2ecf20Sopenharmony_ci	rc = wled4_set_brightness(wled, AUTO_DETECT_BRIGHTNESS);
5998c2ecf20Sopenharmony_ci	if (rc < 0) {
6008c2ecf20Sopenharmony_ci		dev_err(wled->dev, "Failed to set brightness for auto detection rc=%d\n",
6018c2ecf20Sopenharmony_ci			rc);
6028c2ecf20Sopenharmony_ci		goto failed_detect;
6038c2ecf20Sopenharmony_ci	}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	if (wled->cfg.cabc) {
6068c2ecf20Sopenharmony_ci		rc = wled->wled_cabc_config(wled, false);
6078c2ecf20Sopenharmony_ci		if (rc < 0)
6088c2ecf20Sopenharmony_ci			goto failed_detect;
6098c2ecf20Sopenharmony_ci	}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	/* Disable all sinks */
6128c2ecf20Sopenharmony_ci	rc = regmap_write(wled->regmap,
6138c2ecf20Sopenharmony_ci			  wled->sink_addr + WLED4_SINK_REG_CURR_SINK, 0);
6148c2ecf20Sopenharmony_ci	if (rc < 0) {
6158c2ecf20Sopenharmony_ci		dev_err(wled->dev, "Failed to disable all sinks rc=%d\n", rc);
6168c2ecf20Sopenharmony_ci		goto failed_detect;
6178c2ecf20Sopenharmony_ci	}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	/* Iterate through the strings one by one */
6208c2ecf20Sopenharmony_ci	for (i = 0; i < wled->cfg.num_strings; i++) {
6218c2ecf20Sopenharmony_ci		j = wled->cfg.enabled_strings[i];
6228c2ecf20Sopenharmony_ci		sink_test = BIT((WLED4_SINK_REG_CURR_SINK_SHFT + j));
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci		/* Enable feedback control */
6258c2ecf20Sopenharmony_ci		rc = regmap_write(wled->regmap, wled->ctrl_addr +
6268c2ecf20Sopenharmony_ci				  WLED3_CTRL_REG_FEEDBACK_CONTROL, j + 1);
6278c2ecf20Sopenharmony_ci		if (rc < 0) {
6288c2ecf20Sopenharmony_ci			dev_err(wled->dev, "Failed to enable feedback for SINK %d rc = %d\n",
6298c2ecf20Sopenharmony_ci				j + 1, rc);
6308c2ecf20Sopenharmony_ci			goto failed_detect;
6318c2ecf20Sopenharmony_ci		}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci		/* Enable the sink */
6348c2ecf20Sopenharmony_ci		rc = regmap_write(wled->regmap, wled->sink_addr +
6358c2ecf20Sopenharmony_ci				  WLED4_SINK_REG_CURR_SINK, sink_test);
6368c2ecf20Sopenharmony_ci		if (rc < 0) {
6378c2ecf20Sopenharmony_ci			dev_err(wled->dev, "Failed to configure SINK %d rc=%d\n",
6388c2ecf20Sopenharmony_ci				j + 1, rc);
6398c2ecf20Sopenharmony_ci			goto failed_detect;
6408c2ecf20Sopenharmony_ci		}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci		/* Enable the module */
6438c2ecf20Sopenharmony_ci		rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
6448c2ecf20Sopenharmony_ci					WLED3_CTRL_REG_MOD_EN,
6458c2ecf20Sopenharmony_ci					WLED3_CTRL_REG_MOD_EN_MASK,
6468c2ecf20Sopenharmony_ci					WLED3_CTRL_REG_MOD_EN_MASK);
6478c2ecf20Sopenharmony_ci		if (rc < 0) {
6488c2ecf20Sopenharmony_ci			dev_err(wled->dev, "Failed to enable WLED module rc=%d\n",
6498c2ecf20Sopenharmony_ci				rc);
6508c2ecf20Sopenharmony_ci			goto failed_detect;
6518c2ecf20Sopenharmony_ci		}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci		delay_time_us = wled->wled_ovp_delay(wled);
6548c2ecf20Sopenharmony_ci		usleep_range(delay_time_us, delay_time_us + 1000);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci		rc = wled_ovp_fault_status(wled, &fault_set);
6578c2ecf20Sopenharmony_ci		if (rc < 0) {
6588c2ecf20Sopenharmony_ci			dev_err(wled->dev, "Error in getting OVP fault_sts, rc=%d\n",
6598c2ecf20Sopenharmony_ci				rc);
6608c2ecf20Sopenharmony_ci			goto failed_detect;
6618c2ecf20Sopenharmony_ci		}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci		if (fault_set)
6648c2ecf20Sopenharmony_ci			dev_dbg(wled->dev, "WLED OVP fault detected with SINK %d\n",
6658c2ecf20Sopenharmony_ci				j + 1);
6668c2ecf20Sopenharmony_ci		else
6678c2ecf20Sopenharmony_ci			sink_valid |= sink_test;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci		/* Disable the module */
6708c2ecf20Sopenharmony_ci		rc = regmap_update_bits(wled->regmap,
6718c2ecf20Sopenharmony_ci					wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN,
6728c2ecf20Sopenharmony_ci					WLED3_CTRL_REG_MOD_EN_MASK, 0);
6738c2ecf20Sopenharmony_ci		if (rc < 0) {
6748c2ecf20Sopenharmony_ci			dev_err(wled->dev, "Failed to disable WLED module rc=%d\n",
6758c2ecf20Sopenharmony_ci				rc);
6768c2ecf20Sopenharmony_ci			goto failed_detect;
6778c2ecf20Sopenharmony_ci		}
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	if (!sink_valid) {
6818c2ecf20Sopenharmony_ci		dev_err(wled->dev, "No valid WLED sinks found\n");
6828c2ecf20Sopenharmony_ci		wled->disabled_by_short = true;
6838c2ecf20Sopenharmony_ci		goto failed_detect;
6848c2ecf20Sopenharmony_ci	}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	if (sink_valid != sink_config) {
6878c2ecf20Sopenharmony_ci		dev_warn(wled->dev, "%x is not a valid sink configuration - using %x instead\n",
6888c2ecf20Sopenharmony_ci			 sink_config, sink_valid);
6898c2ecf20Sopenharmony_ci		sink_config = sink_valid;
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	/* Write the new sink configuration */
6938c2ecf20Sopenharmony_ci	rc = regmap_write(wled->regmap,
6948c2ecf20Sopenharmony_ci			  wled->sink_addr + WLED4_SINK_REG_CURR_SINK,
6958c2ecf20Sopenharmony_ci			  sink_config);
6968c2ecf20Sopenharmony_ci	if (rc < 0) {
6978c2ecf20Sopenharmony_ci		dev_err(wled->dev, "Failed to reconfigure the default sink rc=%d\n",
6988c2ecf20Sopenharmony_ci			rc);
6998c2ecf20Sopenharmony_ci		goto failed_detect;
7008c2ecf20Sopenharmony_ci	}
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	/* Enable valid sinks */
7038c2ecf20Sopenharmony_ci	if (wled->version == 4) {
7048c2ecf20Sopenharmony_ci		for (i = 0; i < wled->cfg.num_strings; i++) {
7058c2ecf20Sopenharmony_ci			j = wled->cfg.enabled_strings[i];
7068c2ecf20Sopenharmony_ci			if (sink_config &
7078c2ecf20Sopenharmony_ci			    BIT(WLED4_SINK_REG_CURR_SINK_SHFT + j))
7088c2ecf20Sopenharmony_ci				val = WLED4_SINK_REG_STR_MOD_MASK;
7098c2ecf20Sopenharmony_ci			else
7108c2ecf20Sopenharmony_ci				/* Disable modulator_en for unused sink */
7118c2ecf20Sopenharmony_ci				val = 0;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci			rc = regmap_write(wled->regmap, wled->sink_addr +
7148c2ecf20Sopenharmony_ci					  WLED4_SINK_REG_STR_MOD_EN(j), val);
7158c2ecf20Sopenharmony_ci			if (rc < 0) {
7168c2ecf20Sopenharmony_ci				dev_err(wled->dev, "Failed to configure MODULATOR_EN rc=%d\n",
7178c2ecf20Sopenharmony_ci					rc);
7188c2ecf20Sopenharmony_ci				goto failed_detect;
7198c2ecf20Sopenharmony_ci			}
7208c2ecf20Sopenharmony_ci		}
7218c2ecf20Sopenharmony_ci	}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	/* Enable CABC */
7248c2ecf20Sopenharmony_ci	rc = wled->wled_cabc_config(wled, true);
7258c2ecf20Sopenharmony_ci	if (rc < 0)
7268c2ecf20Sopenharmony_ci		goto failed_detect;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	/* Restore the feedback setting */
7298c2ecf20Sopenharmony_ci	rc = regmap_write(wled->regmap,
7308c2ecf20Sopenharmony_ci			  wled->ctrl_addr + WLED3_CTRL_REG_FEEDBACK_CONTROL, 0);
7318c2ecf20Sopenharmony_ci	if (rc < 0) {
7328c2ecf20Sopenharmony_ci		dev_err(wled->dev, "Failed to restore feedback setting rc=%d\n",
7338c2ecf20Sopenharmony_ci			rc);
7348c2ecf20Sopenharmony_ci		goto failed_detect;
7358c2ecf20Sopenharmony_ci	}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	/* Restore brightness */
7388c2ecf20Sopenharmony_ci	rc = wled4_set_brightness(wled, wled->brightness);
7398c2ecf20Sopenharmony_ci	if (rc < 0) {
7408c2ecf20Sopenharmony_ci		dev_err(wled->dev, "Failed to set brightness after auto detection rc=%d\n",
7418c2ecf20Sopenharmony_ci			rc);
7428c2ecf20Sopenharmony_ci		goto failed_detect;
7438c2ecf20Sopenharmony_ci	}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap,
7468c2ecf20Sopenharmony_ci				wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN,
7478c2ecf20Sopenharmony_ci				WLED3_CTRL_REG_MOD_EN_MASK,
7488c2ecf20Sopenharmony_ci				WLED3_CTRL_REG_MOD_EN_MASK);
7498c2ecf20Sopenharmony_ci	if (rc < 0) {
7508c2ecf20Sopenharmony_ci		dev_err(wled->dev, "Failed to enable WLED module rc=%d\n", rc);
7518c2ecf20Sopenharmony_ci		goto failed_detect;
7528c2ecf20Sopenharmony_ci	}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_cifailed_detect:
7558c2ecf20Sopenharmony_ci	return;
7568c2ecf20Sopenharmony_ci}
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci#define WLED_AUTO_DETECT_OVP_COUNT		5
7598c2ecf20Sopenharmony_ci#define WLED_AUTO_DETECT_CNT_DLY_US		USEC_PER_SEC
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_cistatic bool wled4_auto_detection_required(struct wled *wled)
7628c2ecf20Sopenharmony_ci{
7638c2ecf20Sopenharmony_ci	s64 elapsed_time_us;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	if (!wled->cfg.auto_detection_enabled)
7668c2ecf20Sopenharmony_ci		return false;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	/*
7698c2ecf20Sopenharmony_ci	 * Check if the OVP fault was an occasional one
7708c2ecf20Sopenharmony_ci	 * or if it's firing continuously, the latter qualifies
7718c2ecf20Sopenharmony_ci	 * for an auto-detection check.
7728c2ecf20Sopenharmony_ci	 */
7738c2ecf20Sopenharmony_ci	if (!wled->auto_detection_ovp_count) {
7748c2ecf20Sopenharmony_ci		wled->start_ovp_fault_time = ktime_get();
7758c2ecf20Sopenharmony_ci		wled->auto_detection_ovp_count++;
7768c2ecf20Sopenharmony_ci	} else {
7778c2ecf20Sopenharmony_ci		elapsed_time_us = ktime_us_delta(ktime_get(),
7788c2ecf20Sopenharmony_ci						 wled->start_ovp_fault_time);
7798c2ecf20Sopenharmony_ci		if (elapsed_time_us > WLED_AUTO_DETECT_CNT_DLY_US)
7808c2ecf20Sopenharmony_ci			wled->auto_detection_ovp_count = 0;
7818c2ecf20Sopenharmony_ci		else
7828c2ecf20Sopenharmony_ci			wled->auto_detection_ovp_count++;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci		if (wled->auto_detection_ovp_count >=
7858c2ecf20Sopenharmony_ci				WLED_AUTO_DETECT_OVP_COUNT) {
7868c2ecf20Sopenharmony_ci			wled->auto_detection_ovp_count = 0;
7878c2ecf20Sopenharmony_ci			return true;
7888c2ecf20Sopenharmony_ci		}
7898c2ecf20Sopenharmony_ci	}
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	return false;
7928c2ecf20Sopenharmony_ci}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_cistatic bool wled5_auto_detection_required(struct wled *wled)
7958c2ecf20Sopenharmony_ci{
7968c2ecf20Sopenharmony_ci	if (!wled->cfg.auto_detection_enabled)
7978c2ecf20Sopenharmony_ci		return false;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	/*
8008c2ecf20Sopenharmony_ci	 * Unlike WLED4, WLED5 has OVP fault density interrupt configuration
8018c2ecf20Sopenharmony_ci	 * i.e. to count the number of OVP alarms for a certain duration before
8028c2ecf20Sopenharmony_ci	 * triggering OVP fault interrupt. By default, number of OVP fault
8038c2ecf20Sopenharmony_ci	 * events counted before an interrupt is fired is 32 and the time
8048c2ecf20Sopenharmony_ci	 * interval is 12 ms. If we see one OVP fault interrupt, then that
8058c2ecf20Sopenharmony_ci	 * should qualify for a real OVP fault condition to run auto detection
8068c2ecf20Sopenharmony_ci	 * algorithm.
8078c2ecf20Sopenharmony_ci	 */
8088c2ecf20Sopenharmony_ci	return true;
8098c2ecf20Sopenharmony_ci}
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_cistatic int wled_auto_detection_at_init(struct wled *wled)
8128c2ecf20Sopenharmony_ci{
8138c2ecf20Sopenharmony_ci	int rc;
8148c2ecf20Sopenharmony_ci	bool fault_set;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	if (!wled->cfg.auto_detection_enabled)
8178c2ecf20Sopenharmony_ci		return 0;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	rc = wled_ovp_fault_status(wled, &fault_set);
8208c2ecf20Sopenharmony_ci	if (rc < 0) {
8218c2ecf20Sopenharmony_ci		dev_err(wled->dev, "Error in getting OVP fault_sts, rc=%d\n",
8228c2ecf20Sopenharmony_ci			rc);
8238c2ecf20Sopenharmony_ci		return rc;
8248c2ecf20Sopenharmony_ci	}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	if (fault_set) {
8278c2ecf20Sopenharmony_ci		mutex_lock(&wled->lock);
8288c2ecf20Sopenharmony_ci		wled_auto_string_detection(wled);
8298c2ecf20Sopenharmony_ci		mutex_unlock(&wled->lock);
8308c2ecf20Sopenharmony_ci	}
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	return rc;
8338c2ecf20Sopenharmony_ci}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_cistatic irqreturn_t wled_ovp_irq_handler(int irq, void *_wled)
8368c2ecf20Sopenharmony_ci{
8378c2ecf20Sopenharmony_ci	struct wled *wled = _wled;
8388c2ecf20Sopenharmony_ci	int rc;
8398c2ecf20Sopenharmony_ci	u32 int_sts, fault_sts;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	rc = regmap_read(wled->regmap,
8428c2ecf20Sopenharmony_ci			 wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS, &int_sts);
8438c2ecf20Sopenharmony_ci	if (rc < 0) {
8448c2ecf20Sopenharmony_ci		dev_err(wled->dev, "Error in reading WLED3_INT_RT_STS rc=%d\n",
8458c2ecf20Sopenharmony_ci			rc);
8468c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
8478c2ecf20Sopenharmony_ci	}
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	rc = regmap_read(wled->regmap, wled->ctrl_addr +
8508c2ecf20Sopenharmony_ci			 WLED3_CTRL_REG_FAULT_STATUS, &fault_sts);
8518c2ecf20Sopenharmony_ci	if (rc < 0) {
8528c2ecf20Sopenharmony_ci		dev_err(wled->dev, "Error in reading WLED_FAULT_STATUS rc=%d\n",
8538c2ecf20Sopenharmony_ci			rc);
8548c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
8558c2ecf20Sopenharmony_ci	}
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	if (fault_sts & (WLED3_CTRL_REG_OVP_FAULT_BIT |
8588c2ecf20Sopenharmony_ci		WLED3_CTRL_REG_ILIM_FAULT_BIT))
8598c2ecf20Sopenharmony_ci		dev_dbg(wled->dev, "WLED OVP fault detected, int_sts=%x fault_sts= %x\n",
8608c2ecf20Sopenharmony_ci			int_sts, fault_sts);
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	if (fault_sts & WLED3_CTRL_REG_OVP_FAULT_BIT) {
8638c2ecf20Sopenharmony_ci		if (wled->wled_auto_detection_required(wled)) {
8648c2ecf20Sopenharmony_ci			mutex_lock(&wled->lock);
8658c2ecf20Sopenharmony_ci			wled_auto_string_detection(wled);
8668c2ecf20Sopenharmony_ci			mutex_unlock(&wled->lock);
8678c2ecf20Sopenharmony_ci		}
8688c2ecf20Sopenharmony_ci	}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
8718c2ecf20Sopenharmony_ci}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_cistatic int wled3_setup(struct wled *wled)
8748c2ecf20Sopenharmony_ci{
8758c2ecf20Sopenharmony_ci	u16 addr;
8768c2ecf20Sopenharmony_ci	u8 sink_en = 0;
8778c2ecf20Sopenharmony_ci	int rc, i, j;
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap,
8808c2ecf20Sopenharmony_ci				wled->ctrl_addr + WLED3_CTRL_REG_OVP,
8818c2ecf20Sopenharmony_ci				WLED3_CTRL_REG_OVP_MASK, wled->cfg.ovp);
8828c2ecf20Sopenharmony_ci	if (rc)
8838c2ecf20Sopenharmony_ci		return rc;
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap,
8868c2ecf20Sopenharmony_ci				wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT,
8878c2ecf20Sopenharmony_ci				WLED3_CTRL_REG_ILIMIT_MASK,
8888c2ecf20Sopenharmony_ci				wled->cfg.boost_i_limit);
8898c2ecf20Sopenharmony_ci	if (rc)
8908c2ecf20Sopenharmony_ci		return rc;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap,
8938c2ecf20Sopenharmony_ci				wled->ctrl_addr + WLED3_CTRL_REG_FREQ,
8948c2ecf20Sopenharmony_ci				WLED3_CTRL_REG_FREQ_MASK,
8958c2ecf20Sopenharmony_ci				wled->cfg.switch_freq);
8968c2ecf20Sopenharmony_ci	if (rc)
8978c2ecf20Sopenharmony_ci		return rc;
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	for (i = 0; i < wled->cfg.num_strings; ++i) {
9008c2ecf20Sopenharmony_ci		j = wled->cfg.enabled_strings[i];
9018c2ecf20Sopenharmony_ci		addr = wled->ctrl_addr + WLED3_SINK_REG_STR_MOD_EN(j);
9028c2ecf20Sopenharmony_ci		rc = regmap_update_bits(wled->regmap, addr,
9038c2ecf20Sopenharmony_ci					WLED3_SINK_REG_STR_MOD_MASK,
9048c2ecf20Sopenharmony_ci					WLED3_SINK_REG_STR_MOD_MASK);
9058c2ecf20Sopenharmony_ci		if (rc)
9068c2ecf20Sopenharmony_ci			return rc;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci		if (wled->cfg.ext_gen) {
9098c2ecf20Sopenharmony_ci			addr = wled->ctrl_addr + WLED3_SINK_REG_STR_MOD_SRC(j);
9108c2ecf20Sopenharmony_ci			rc = regmap_update_bits(wled->regmap, addr,
9118c2ecf20Sopenharmony_ci						WLED3_SINK_REG_STR_MOD_SRC_MASK,
9128c2ecf20Sopenharmony_ci						WLED3_SINK_REG_STR_MOD_SRC_EXT);
9138c2ecf20Sopenharmony_ci			if (rc)
9148c2ecf20Sopenharmony_ci				return rc;
9158c2ecf20Sopenharmony_ci		}
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci		addr = wled->ctrl_addr + WLED3_SINK_REG_STR_FULL_SCALE_CURR(j);
9188c2ecf20Sopenharmony_ci		rc = regmap_update_bits(wled->regmap, addr,
9198c2ecf20Sopenharmony_ci					WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK,
9208c2ecf20Sopenharmony_ci					wled->cfg.string_i_limit);
9218c2ecf20Sopenharmony_ci		if (rc)
9228c2ecf20Sopenharmony_ci			return rc;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci		addr = wled->ctrl_addr + WLED3_SINK_REG_STR_CABC(j);
9258c2ecf20Sopenharmony_ci		rc = regmap_update_bits(wled->regmap, addr,
9268c2ecf20Sopenharmony_ci					WLED3_SINK_REG_STR_CABC_MASK,
9278c2ecf20Sopenharmony_ci					wled->cfg.cabc ?
9288c2ecf20Sopenharmony_ci					WLED3_SINK_REG_STR_CABC_MASK : 0);
9298c2ecf20Sopenharmony_ci		if (rc)
9308c2ecf20Sopenharmony_ci			return rc;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci		sink_en |= BIT(j + WLED3_SINK_REG_CURR_SINK_SHFT);
9338c2ecf20Sopenharmony_ci	}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap,
9368c2ecf20Sopenharmony_ci				wled->ctrl_addr + WLED3_SINK_REG_CURR_SINK,
9378c2ecf20Sopenharmony_ci				WLED3_SINK_REG_CURR_SINK_MASK, sink_en);
9388c2ecf20Sopenharmony_ci	if (rc)
9398c2ecf20Sopenharmony_ci		return rc;
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	return 0;
9428c2ecf20Sopenharmony_ci}
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_cistatic const struct wled_config wled3_config_defaults = {
9458c2ecf20Sopenharmony_ci	.boost_i_limit = 3,
9468c2ecf20Sopenharmony_ci	.string_i_limit = 20,
9478c2ecf20Sopenharmony_ci	.ovp = 2,
9488c2ecf20Sopenharmony_ci	.num_strings = 3,
9498c2ecf20Sopenharmony_ci	.switch_freq = 5,
9508c2ecf20Sopenharmony_ci	.cs_out_en = false,
9518c2ecf20Sopenharmony_ci	.ext_gen = false,
9528c2ecf20Sopenharmony_ci	.cabc = false,
9538c2ecf20Sopenharmony_ci	.enabled_strings = {0, 1, 2, 3},
9548c2ecf20Sopenharmony_ci};
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_cistatic int wled4_setup(struct wled *wled)
9578c2ecf20Sopenharmony_ci{
9588c2ecf20Sopenharmony_ci	int rc, temp, i, j;
9598c2ecf20Sopenharmony_ci	u16 addr;
9608c2ecf20Sopenharmony_ci	u8 sink_en = 0;
9618c2ecf20Sopenharmony_ci	u32 sink_cfg;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap,
9648c2ecf20Sopenharmony_ci				wled->ctrl_addr + WLED3_CTRL_REG_OVP,
9658c2ecf20Sopenharmony_ci				WLED3_CTRL_REG_OVP_MASK, wled->cfg.ovp);
9668c2ecf20Sopenharmony_ci	if (rc < 0)
9678c2ecf20Sopenharmony_ci		return rc;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap,
9708c2ecf20Sopenharmony_ci				wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT,
9718c2ecf20Sopenharmony_ci				WLED3_CTRL_REG_ILIMIT_MASK,
9728c2ecf20Sopenharmony_ci				wled->cfg.boost_i_limit);
9738c2ecf20Sopenharmony_ci	if (rc < 0)
9748c2ecf20Sopenharmony_ci		return rc;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap,
9778c2ecf20Sopenharmony_ci				wled->ctrl_addr + WLED3_CTRL_REG_FREQ,
9788c2ecf20Sopenharmony_ci				WLED3_CTRL_REG_FREQ_MASK,
9798c2ecf20Sopenharmony_ci				wled->cfg.switch_freq);
9808c2ecf20Sopenharmony_ci	if (rc < 0)
9818c2ecf20Sopenharmony_ci		return rc;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	if (wled->cfg.external_pfet) {
9848c2ecf20Sopenharmony_ci		/* Unlock the secure register access */
9858c2ecf20Sopenharmony_ci		rc = regmap_write(wled->regmap, wled->ctrl_addr +
9868c2ecf20Sopenharmony_ci				  WLED4_CTRL_REG_SEC_ACCESS,
9878c2ecf20Sopenharmony_ci				  WLED4_CTRL_REG_SEC_UNLOCK);
9888c2ecf20Sopenharmony_ci		if (rc < 0)
9898c2ecf20Sopenharmony_ci			return rc;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci		rc = regmap_write(wled->regmap,
9928c2ecf20Sopenharmony_ci				  wled->ctrl_addr + WLED4_CTRL_REG_TEST1,
9938c2ecf20Sopenharmony_ci				  WLED4_CTRL_REG_TEST1_EXT_FET_DTEST2);
9948c2ecf20Sopenharmony_ci		if (rc < 0)
9958c2ecf20Sopenharmony_ci			return rc;
9968c2ecf20Sopenharmony_ci	}
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	rc = regmap_read(wled->regmap, wled->sink_addr +
9998c2ecf20Sopenharmony_ci			 WLED4_SINK_REG_CURR_SINK, &sink_cfg);
10008c2ecf20Sopenharmony_ci	if (rc < 0)
10018c2ecf20Sopenharmony_ci		return rc;
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	for (i = 0; i < wled->cfg.num_strings; i++) {
10048c2ecf20Sopenharmony_ci		j = wled->cfg.enabled_strings[i];
10058c2ecf20Sopenharmony_ci		temp = j + WLED4_SINK_REG_CURR_SINK_SHFT;
10068c2ecf20Sopenharmony_ci		sink_en |= 1 << temp;
10078c2ecf20Sopenharmony_ci	}
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	if (sink_cfg == sink_en) {
10108c2ecf20Sopenharmony_ci		rc = wled_auto_detection_at_init(wled);
10118c2ecf20Sopenharmony_ci		return rc;
10128c2ecf20Sopenharmony_ci	}
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap,
10158c2ecf20Sopenharmony_ci				wled->sink_addr + WLED4_SINK_REG_CURR_SINK,
10168c2ecf20Sopenharmony_ci				WLED4_SINK_REG_CURR_SINK_MASK, 0);
10178c2ecf20Sopenharmony_ci	if (rc < 0)
10188c2ecf20Sopenharmony_ci		return rc;
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
10218c2ecf20Sopenharmony_ci				WLED3_CTRL_REG_MOD_EN,
10228c2ecf20Sopenharmony_ci				WLED3_CTRL_REG_MOD_EN_MASK, 0);
10238c2ecf20Sopenharmony_ci	if (rc < 0)
10248c2ecf20Sopenharmony_ci		return rc;
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	/* Per sink/string configuration */
10278c2ecf20Sopenharmony_ci	for (i = 0; i < wled->cfg.num_strings; i++) {
10288c2ecf20Sopenharmony_ci		j = wled->cfg.enabled_strings[i];
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci		addr = wled->sink_addr +
10318c2ecf20Sopenharmony_ci				WLED4_SINK_REG_STR_MOD_EN(j);
10328c2ecf20Sopenharmony_ci		rc = regmap_update_bits(wled->regmap, addr,
10338c2ecf20Sopenharmony_ci					WLED4_SINK_REG_STR_MOD_MASK,
10348c2ecf20Sopenharmony_ci					WLED4_SINK_REG_STR_MOD_MASK);
10358c2ecf20Sopenharmony_ci		if (rc < 0)
10368c2ecf20Sopenharmony_ci			return rc;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci		addr = wled->sink_addr +
10398c2ecf20Sopenharmony_ci				WLED4_SINK_REG_STR_FULL_SCALE_CURR(j);
10408c2ecf20Sopenharmony_ci		rc = regmap_update_bits(wled->regmap, addr,
10418c2ecf20Sopenharmony_ci					WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK,
10428c2ecf20Sopenharmony_ci					wled->cfg.string_i_limit);
10438c2ecf20Sopenharmony_ci		if (rc < 0)
10448c2ecf20Sopenharmony_ci			return rc;
10458c2ecf20Sopenharmony_ci	}
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	rc = wled4_cabc_config(wled, wled->cfg.cabc);
10488c2ecf20Sopenharmony_ci	if (rc < 0)
10498c2ecf20Sopenharmony_ci		return rc;
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
10528c2ecf20Sopenharmony_ci				WLED3_CTRL_REG_MOD_EN,
10538c2ecf20Sopenharmony_ci				WLED3_CTRL_REG_MOD_EN_MASK,
10548c2ecf20Sopenharmony_ci				WLED3_CTRL_REG_MOD_EN_MASK);
10558c2ecf20Sopenharmony_ci	if (rc < 0)
10568c2ecf20Sopenharmony_ci		return rc;
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap,
10598c2ecf20Sopenharmony_ci				wled->sink_addr + WLED4_SINK_REG_CURR_SINK,
10608c2ecf20Sopenharmony_ci				WLED4_SINK_REG_CURR_SINK_MASK, sink_en);
10618c2ecf20Sopenharmony_ci	if (rc < 0)
10628c2ecf20Sopenharmony_ci		return rc;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	rc = wled->wled_sync_toggle(wled);
10658c2ecf20Sopenharmony_ci	if (rc < 0) {
10668c2ecf20Sopenharmony_ci		dev_err(wled->dev, "Failed to toggle sync reg rc:%d\n", rc);
10678c2ecf20Sopenharmony_ci		return rc;
10688c2ecf20Sopenharmony_ci	}
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	rc = wled_auto_detection_at_init(wled);
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	return rc;
10738c2ecf20Sopenharmony_ci}
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_cistatic const struct wled_config wled4_config_defaults = {
10768c2ecf20Sopenharmony_ci	.boost_i_limit = 4,
10778c2ecf20Sopenharmony_ci	.string_i_limit = 10,
10788c2ecf20Sopenharmony_ci	.ovp = 1,
10798c2ecf20Sopenharmony_ci	.num_strings = 4,
10808c2ecf20Sopenharmony_ci	.switch_freq = 11,
10818c2ecf20Sopenharmony_ci	.cabc = false,
10828c2ecf20Sopenharmony_ci	.external_pfet = false,
10838c2ecf20Sopenharmony_ci	.auto_detection_enabled = false,
10848c2ecf20Sopenharmony_ci};
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_cistatic int wled5_setup(struct wled *wled)
10878c2ecf20Sopenharmony_ci{
10888c2ecf20Sopenharmony_ci	int rc, temp, i, j, offset;
10898c2ecf20Sopenharmony_ci	u8 sink_en = 0;
10908c2ecf20Sopenharmony_ci	u16 addr;
10918c2ecf20Sopenharmony_ci	u32 val;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap,
10948c2ecf20Sopenharmony_ci				wled->ctrl_addr + WLED3_CTRL_REG_OVP,
10958c2ecf20Sopenharmony_ci				WLED5_CTRL_REG_OVP_MASK, wled->cfg.ovp);
10968c2ecf20Sopenharmony_ci	if (rc < 0)
10978c2ecf20Sopenharmony_ci		return rc;
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap,
11008c2ecf20Sopenharmony_ci				wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT,
11018c2ecf20Sopenharmony_ci				WLED3_CTRL_REG_ILIMIT_MASK,
11028c2ecf20Sopenharmony_ci				wled->cfg.boost_i_limit);
11038c2ecf20Sopenharmony_ci	if (rc < 0)
11048c2ecf20Sopenharmony_ci		return rc;
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap,
11078c2ecf20Sopenharmony_ci				wled->ctrl_addr + WLED3_CTRL_REG_FREQ,
11088c2ecf20Sopenharmony_ci				WLED3_CTRL_REG_FREQ_MASK,
11098c2ecf20Sopenharmony_ci				wled->cfg.switch_freq);
11108c2ecf20Sopenharmony_ci	if (rc < 0)
11118c2ecf20Sopenharmony_ci		return rc;
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	/* Per sink/string configuration */
11148c2ecf20Sopenharmony_ci	for (i = 0; i < wled->cfg.num_strings; ++i) {
11158c2ecf20Sopenharmony_ci		j = wled->cfg.enabled_strings[i];
11168c2ecf20Sopenharmony_ci		addr = wled->sink_addr +
11178c2ecf20Sopenharmony_ci				WLED4_SINK_REG_STR_FULL_SCALE_CURR(j);
11188c2ecf20Sopenharmony_ci		rc = regmap_update_bits(wled->regmap, addr,
11198c2ecf20Sopenharmony_ci					WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK,
11208c2ecf20Sopenharmony_ci					wled->cfg.string_i_limit);
11218c2ecf20Sopenharmony_ci		if (rc < 0)
11228c2ecf20Sopenharmony_ci			return rc;
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci		addr = wled->sink_addr + WLED5_SINK_REG_STR_SRC_SEL(j);
11258c2ecf20Sopenharmony_ci		rc = regmap_update_bits(wled->regmap, addr,
11268c2ecf20Sopenharmony_ci					WLED5_SINK_REG_SRC_SEL_MASK,
11278c2ecf20Sopenharmony_ci					wled->cfg.mod_sel == MOD_A ?
11288c2ecf20Sopenharmony_ci					WLED5_SINK_REG_SRC_SEL_MOD_A :
11298c2ecf20Sopenharmony_ci					WLED5_SINK_REG_SRC_SEL_MOD_B);
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci		temp = j + WLED4_SINK_REG_CURR_SINK_SHFT;
11328c2ecf20Sopenharmony_ci		sink_en |= 1 << temp;
11338c2ecf20Sopenharmony_ci	}
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	rc = wled5_cabc_config(wled, wled->cfg.cabc_sel ? true : false);
11368c2ecf20Sopenharmony_ci	if (rc < 0)
11378c2ecf20Sopenharmony_ci		return rc;
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	/* Enable one of the modulators A or B based on mod_sel */
11408c2ecf20Sopenharmony_ci	addr = wled->sink_addr + WLED5_SINK_REG_MOD_A_EN;
11418c2ecf20Sopenharmony_ci	val = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_REG_MOD_EN_MASK : 0;
11428c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap, addr,
11438c2ecf20Sopenharmony_ci				WLED5_SINK_REG_MOD_EN_MASK, val);
11448c2ecf20Sopenharmony_ci	if (rc < 0)
11458c2ecf20Sopenharmony_ci		return rc;
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	addr = wled->sink_addr + WLED5_SINK_REG_MOD_B_EN;
11488c2ecf20Sopenharmony_ci	val = (wled->cfg.mod_sel == MOD_B) ? WLED5_SINK_REG_MOD_EN_MASK : 0;
11498c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap, addr,
11508c2ecf20Sopenharmony_ci				WLED5_SINK_REG_MOD_EN_MASK, val);
11518c2ecf20Sopenharmony_ci	if (rc < 0)
11528c2ecf20Sopenharmony_ci		return rc;
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	offset = (wled->cfg.mod_sel == MOD_A) ?
11558c2ecf20Sopenharmony_ci		  WLED5_SINK_REG_MOD_A_BRIGHTNESS_WIDTH_SEL :
11568c2ecf20Sopenharmony_ci		  WLED5_SINK_REG_MOD_B_BRIGHTNESS_WIDTH_SEL;
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci	addr = wled->sink_addr + offset;
11598c2ecf20Sopenharmony_ci	val = (wled->max_brightness == WLED5_SINK_REG_BRIGHT_MAX_15B) ?
11608c2ecf20Sopenharmony_ci		 WLED5_SINK_REG_BRIGHTNESS_WIDTH_15B :
11618c2ecf20Sopenharmony_ci		 WLED5_SINK_REG_BRIGHTNESS_WIDTH_12B;
11628c2ecf20Sopenharmony_ci	rc = regmap_write(wled->regmap, addr, val);
11638c2ecf20Sopenharmony_ci	if (rc < 0)
11648c2ecf20Sopenharmony_ci		return rc;
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap,
11678c2ecf20Sopenharmony_ci				wled->sink_addr + WLED4_SINK_REG_CURR_SINK,
11688c2ecf20Sopenharmony_ci				WLED4_SINK_REG_CURR_SINK_MASK, sink_en);
11698c2ecf20Sopenharmony_ci	if (rc < 0)
11708c2ecf20Sopenharmony_ci		return rc;
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci	/* This updates only FSC configuration in WLED5 */
11738c2ecf20Sopenharmony_ci	rc = wled->wled_sync_toggle(wled);
11748c2ecf20Sopenharmony_ci	if (rc < 0) {
11758c2ecf20Sopenharmony_ci		pr_err("Failed to toggle sync reg rc:%d\n", rc);
11768c2ecf20Sopenharmony_ci		return rc;
11778c2ecf20Sopenharmony_ci	}
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	rc = wled_auto_detection_at_init(wled);
11808c2ecf20Sopenharmony_ci	if (rc < 0)
11818c2ecf20Sopenharmony_ci		return rc;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	return 0;
11848c2ecf20Sopenharmony_ci}
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_cistatic const struct wled_config wled5_config_defaults = {
11878c2ecf20Sopenharmony_ci	.boost_i_limit = 5,
11888c2ecf20Sopenharmony_ci	.string_i_limit = 10,
11898c2ecf20Sopenharmony_ci	.ovp = 4,
11908c2ecf20Sopenharmony_ci	.num_strings = 4,
11918c2ecf20Sopenharmony_ci	.switch_freq = 11,
11928c2ecf20Sopenharmony_ci	.mod_sel = 0,
11938c2ecf20Sopenharmony_ci	.cabc_sel = 0,
11948c2ecf20Sopenharmony_ci	.cabc = false,
11958c2ecf20Sopenharmony_ci	.external_pfet = false,
11968c2ecf20Sopenharmony_ci	.auto_detection_enabled = false,
11978c2ecf20Sopenharmony_ci};
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_cistatic const u32 wled3_boost_i_limit_values[] = {
12008c2ecf20Sopenharmony_ci	105, 385, 525, 805, 980, 1260, 1400, 1680,
12018c2ecf20Sopenharmony_ci};
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_cistatic const struct wled_var_cfg wled3_boost_i_limit_cfg = {
12048c2ecf20Sopenharmony_ci	.values = wled3_boost_i_limit_values,
12058c2ecf20Sopenharmony_ci	.size = ARRAY_SIZE(wled3_boost_i_limit_values),
12068c2ecf20Sopenharmony_ci};
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_cistatic const u32 wled4_boost_i_limit_values[] = {
12098c2ecf20Sopenharmony_ci	105, 280, 450, 620, 970, 1150, 1300, 1500,
12108c2ecf20Sopenharmony_ci};
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_cistatic const struct wled_var_cfg wled4_boost_i_limit_cfg = {
12138c2ecf20Sopenharmony_ci	.values = wled4_boost_i_limit_values,
12148c2ecf20Sopenharmony_ci	.size = ARRAY_SIZE(wled4_boost_i_limit_values),
12158c2ecf20Sopenharmony_ci};
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_cistatic inline u32 wled5_boost_i_limit_values_fn(u32 idx)
12188c2ecf20Sopenharmony_ci{
12198c2ecf20Sopenharmony_ci	return 525 + (idx * 175);
12208c2ecf20Sopenharmony_ci}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_cistatic const struct wled_var_cfg wled5_boost_i_limit_cfg = {
12238c2ecf20Sopenharmony_ci	.fn = wled5_boost_i_limit_values_fn,
12248c2ecf20Sopenharmony_ci	.size = 8,
12258c2ecf20Sopenharmony_ci};
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_cistatic const u32 wled3_ovp_values[] = {
12288c2ecf20Sopenharmony_ci	35, 32, 29, 27,
12298c2ecf20Sopenharmony_ci};
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_cistatic const struct wled_var_cfg wled3_ovp_cfg = {
12328c2ecf20Sopenharmony_ci	.values = wled3_ovp_values,
12338c2ecf20Sopenharmony_ci	.size = ARRAY_SIZE(wled3_ovp_values),
12348c2ecf20Sopenharmony_ci};
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_cistatic const u32 wled4_ovp_values[] = {
12378c2ecf20Sopenharmony_ci	31100, 29600, 19600, 18100,
12388c2ecf20Sopenharmony_ci};
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_cistatic const struct wled_var_cfg wled4_ovp_cfg = {
12418c2ecf20Sopenharmony_ci	.values = wled4_ovp_values,
12428c2ecf20Sopenharmony_ci	.size = ARRAY_SIZE(wled4_ovp_values),
12438c2ecf20Sopenharmony_ci};
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_cistatic inline u32 wled5_ovp_values_fn(u32 idx)
12468c2ecf20Sopenharmony_ci{
12478c2ecf20Sopenharmony_ci	/*
12488c2ecf20Sopenharmony_ci	 * 0000 - 38.5 V
12498c2ecf20Sopenharmony_ci	 * 0001 - 37 V ..
12508c2ecf20Sopenharmony_ci	 * 1111 - 16 V
12518c2ecf20Sopenharmony_ci	 */
12528c2ecf20Sopenharmony_ci	return 38500 - (idx * 1500);
12538c2ecf20Sopenharmony_ci}
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_cistatic const struct wled_var_cfg wled5_ovp_cfg = {
12568c2ecf20Sopenharmony_ci	.fn = wled5_ovp_values_fn,
12578c2ecf20Sopenharmony_ci	.size = 16,
12588c2ecf20Sopenharmony_ci};
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_cistatic u32 wled3_switch_freq_values_fn(u32 idx)
12618c2ecf20Sopenharmony_ci{
12628c2ecf20Sopenharmony_ci	return 19200 / (2 * (1 + idx));
12638c2ecf20Sopenharmony_ci}
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_cistatic const struct wled_var_cfg wled3_switch_freq_cfg = {
12668c2ecf20Sopenharmony_ci	.fn = wled3_switch_freq_values_fn,
12678c2ecf20Sopenharmony_ci	.size = 16,
12688c2ecf20Sopenharmony_ci};
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_cistatic const struct wled_var_cfg wled3_string_i_limit_cfg = {
12718c2ecf20Sopenharmony_ci	.size = 26,
12728c2ecf20Sopenharmony_ci};
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_cistatic const u32 wled4_string_i_limit_values[] = {
12758c2ecf20Sopenharmony_ci	0, 2500, 5000, 7500, 10000, 12500, 15000, 17500, 20000,
12768c2ecf20Sopenharmony_ci	22500, 25000, 27500, 30000,
12778c2ecf20Sopenharmony_ci};
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_cistatic const struct wled_var_cfg wled4_string_i_limit_cfg = {
12808c2ecf20Sopenharmony_ci	.values = wled4_string_i_limit_values,
12818c2ecf20Sopenharmony_ci	.size = ARRAY_SIZE(wled4_string_i_limit_values),
12828c2ecf20Sopenharmony_ci};
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_cistatic const struct wled_var_cfg wled5_mod_sel_cfg = {
12858c2ecf20Sopenharmony_ci	.size = 2,
12868c2ecf20Sopenharmony_ci};
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_cistatic const struct wled_var_cfg wled5_cabc_sel_cfg = {
12898c2ecf20Sopenharmony_ci	.size = 4,
12908c2ecf20Sopenharmony_ci};
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_cistatic u32 wled_values(const struct wled_var_cfg *cfg, u32 idx)
12938c2ecf20Sopenharmony_ci{
12948c2ecf20Sopenharmony_ci	if (idx >= cfg->size)
12958c2ecf20Sopenharmony_ci		return UINT_MAX;
12968c2ecf20Sopenharmony_ci	if (cfg->fn)
12978c2ecf20Sopenharmony_ci		return cfg->fn(idx);
12988c2ecf20Sopenharmony_ci	if (cfg->values)
12998c2ecf20Sopenharmony_ci		return cfg->values[idx];
13008c2ecf20Sopenharmony_ci	return idx;
13018c2ecf20Sopenharmony_ci}
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_cistatic int wled_configure(struct wled *wled)
13048c2ecf20Sopenharmony_ci{
13058c2ecf20Sopenharmony_ci	struct wled_config *cfg = &wled->cfg;
13068c2ecf20Sopenharmony_ci	struct device *dev = wled->dev;
13078c2ecf20Sopenharmony_ci	const __be32 *prop_addr;
13088c2ecf20Sopenharmony_ci	u32 size, val, c;
13098c2ecf20Sopenharmony_ci	int rc, i, j, string_len;
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci	const struct wled_u32_opts *u32_opts = NULL;
13128c2ecf20Sopenharmony_ci	const struct wled_u32_opts wled3_opts[] = {
13138c2ecf20Sopenharmony_ci		{
13148c2ecf20Sopenharmony_ci			.name = "qcom,current-boost-limit",
13158c2ecf20Sopenharmony_ci			.val_ptr = &cfg->boost_i_limit,
13168c2ecf20Sopenharmony_ci			.cfg = &wled3_boost_i_limit_cfg,
13178c2ecf20Sopenharmony_ci		},
13188c2ecf20Sopenharmony_ci		{
13198c2ecf20Sopenharmony_ci			.name = "qcom,current-limit",
13208c2ecf20Sopenharmony_ci			.val_ptr = &cfg->string_i_limit,
13218c2ecf20Sopenharmony_ci			.cfg = &wled3_string_i_limit_cfg,
13228c2ecf20Sopenharmony_ci		},
13238c2ecf20Sopenharmony_ci		{
13248c2ecf20Sopenharmony_ci			.name = "qcom,ovp",
13258c2ecf20Sopenharmony_ci			.val_ptr = &cfg->ovp,
13268c2ecf20Sopenharmony_ci			.cfg = &wled3_ovp_cfg,
13278c2ecf20Sopenharmony_ci		},
13288c2ecf20Sopenharmony_ci		{
13298c2ecf20Sopenharmony_ci			.name = "qcom,switching-freq",
13308c2ecf20Sopenharmony_ci			.val_ptr = &cfg->switch_freq,
13318c2ecf20Sopenharmony_ci			.cfg = &wled3_switch_freq_cfg,
13328c2ecf20Sopenharmony_ci		},
13338c2ecf20Sopenharmony_ci	};
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci	const struct wled_u32_opts wled4_opts[] = {
13368c2ecf20Sopenharmony_ci		{
13378c2ecf20Sopenharmony_ci			.name = "qcom,current-boost-limit",
13388c2ecf20Sopenharmony_ci			.val_ptr = &cfg->boost_i_limit,
13398c2ecf20Sopenharmony_ci			.cfg = &wled4_boost_i_limit_cfg,
13408c2ecf20Sopenharmony_ci		},
13418c2ecf20Sopenharmony_ci		{
13428c2ecf20Sopenharmony_ci			.name = "qcom,current-limit-microamp",
13438c2ecf20Sopenharmony_ci			.val_ptr = &cfg->string_i_limit,
13448c2ecf20Sopenharmony_ci			.cfg = &wled4_string_i_limit_cfg,
13458c2ecf20Sopenharmony_ci		},
13468c2ecf20Sopenharmony_ci		{
13478c2ecf20Sopenharmony_ci			.name = "qcom,ovp-millivolt",
13488c2ecf20Sopenharmony_ci			.val_ptr = &cfg->ovp,
13498c2ecf20Sopenharmony_ci			.cfg = &wled4_ovp_cfg,
13508c2ecf20Sopenharmony_ci		},
13518c2ecf20Sopenharmony_ci		{
13528c2ecf20Sopenharmony_ci			.name = "qcom,switching-freq",
13538c2ecf20Sopenharmony_ci			.val_ptr = &cfg->switch_freq,
13548c2ecf20Sopenharmony_ci			.cfg = &wled3_switch_freq_cfg,
13558c2ecf20Sopenharmony_ci		},
13568c2ecf20Sopenharmony_ci	};
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	const struct wled_u32_opts wled5_opts[] = {
13598c2ecf20Sopenharmony_ci		{
13608c2ecf20Sopenharmony_ci			.name = "qcom,current-boost-limit",
13618c2ecf20Sopenharmony_ci			.val_ptr = &cfg->boost_i_limit,
13628c2ecf20Sopenharmony_ci			.cfg = &wled5_boost_i_limit_cfg,
13638c2ecf20Sopenharmony_ci		},
13648c2ecf20Sopenharmony_ci		{
13658c2ecf20Sopenharmony_ci			.name = "qcom,current-limit-microamp",
13668c2ecf20Sopenharmony_ci			.val_ptr = &cfg->string_i_limit,
13678c2ecf20Sopenharmony_ci			.cfg = &wled4_string_i_limit_cfg,
13688c2ecf20Sopenharmony_ci		},
13698c2ecf20Sopenharmony_ci		{
13708c2ecf20Sopenharmony_ci			.name = "qcom,ovp-millivolt",
13718c2ecf20Sopenharmony_ci			.val_ptr = &cfg->ovp,
13728c2ecf20Sopenharmony_ci			.cfg = &wled5_ovp_cfg,
13738c2ecf20Sopenharmony_ci		},
13748c2ecf20Sopenharmony_ci		{
13758c2ecf20Sopenharmony_ci			.name = "qcom,switching-freq",
13768c2ecf20Sopenharmony_ci			.val_ptr = &cfg->switch_freq,
13778c2ecf20Sopenharmony_ci			.cfg = &wled3_switch_freq_cfg,
13788c2ecf20Sopenharmony_ci		},
13798c2ecf20Sopenharmony_ci		{
13808c2ecf20Sopenharmony_ci			.name = "qcom,modulator-sel",
13818c2ecf20Sopenharmony_ci			.val_ptr = &cfg->mod_sel,
13828c2ecf20Sopenharmony_ci			.cfg = &wled5_mod_sel_cfg,
13838c2ecf20Sopenharmony_ci		},
13848c2ecf20Sopenharmony_ci		{
13858c2ecf20Sopenharmony_ci			.name = "qcom,cabc-sel",
13868c2ecf20Sopenharmony_ci			.val_ptr = &cfg->cabc_sel,
13878c2ecf20Sopenharmony_ci			.cfg = &wled5_cabc_sel_cfg,
13888c2ecf20Sopenharmony_ci		},
13898c2ecf20Sopenharmony_ci	};
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	const struct wled_bool_opts bool_opts[] = {
13928c2ecf20Sopenharmony_ci		{ "qcom,cs-out", &cfg->cs_out_en, },
13938c2ecf20Sopenharmony_ci		{ "qcom,ext-gen", &cfg->ext_gen, },
13948c2ecf20Sopenharmony_ci		{ "qcom,cabc", &cfg->cabc, },
13958c2ecf20Sopenharmony_ci		{ "qcom,external-pfet", &cfg->external_pfet, },
13968c2ecf20Sopenharmony_ci		{ "qcom,auto-string-detection", &cfg->auto_detection_enabled, },
13978c2ecf20Sopenharmony_ci	};
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	prop_addr = of_get_address(dev->of_node, 0, NULL, NULL);
14008c2ecf20Sopenharmony_ci	if (!prop_addr) {
14018c2ecf20Sopenharmony_ci		dev_err(wled->dev, "invalid IO resources\n");
14028c2ecf20Sopenharmony_ci		return -EINVAL;
14038c2ecf20Sopenharmony_ci	}
14048c2ecf20Sopenharmony_ci	wled->ctrl_addr = be32_to_cpu(*prop_addr);
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	rc = of_property_read_string(dev->of_node, "label", &wled->name);
14078c2ecf20Sopenharmony_ci	if (rc)
14088c2ecf20Sopenharmony_ci		wled->name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node);
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci	switch (wled->version) {
14118c2ecf20Sopenharmony_ci	case 3:
14128c2ecf20Sopenharmony_ci		u32_opts = wled3_opts;
14138c2ecf20Sopenharmony_ci		size = ARRAY_SIZE(wled3_opts);
14148c2ecf20Sopenharmony_ci		*cfg = wled3_config_defaults;
14158c2ecf20Sopenharmony_ci		wled->wled_set_brightness = wled3_set_brightness;
14168c2ecf20Sopenharmony_ci		wled->wled_sync_toggle = wled3_sync_toggle;
14178c2ecf20Sopenharmony_ci		wled->max_string_count = 3;
14188c2ecf20Sopenharmony_ci		wled->sink_addr = wled->ctrl_addr;
14198c2ecf20Sopenharmony_ci		break;
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	case 4:
14228c2ecf20Sopenharmony_ci		u32_opts = wled4_opts;
14238c2ecf20Sopenharmony_ci		size = ARRAY_SIZE(wled4_opts);
14248c2ecf20Sopenharmony_ci		*cfg = wled4_config_defaults;
14258c2ecf20Sopenharmony_ci		wled->wled_set_brightness = wled4_set_brightness;
14268c2ecf20Sopenharmony_ci		wled->wled_sync_toggle = wled3_sync_toggle;
14278c2ecf20Sopenharmony_ci		wled->wled_cabc_config = wled4_cabc_config;
14288c2ecf20Sopenharmony_ci		wled->wled_ovp_delay = wled4_ovp_delay;
14298c2ecf20Sopenharmony_ci		wled->wled_auto_detection_required =
14308c2ecf20Sopenharmony_ci					wled4_auto_detection_required;
14318c2ecf20Sopenharmony_ci		wled->max_string_count = 4;
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci		prop_addr = of_get_address(dev->of_node, 1, NULL, NULL);
14348c2ecf20Sopenharmony_ci		if (!prop_addr) {
14358c2ecf20Sopenharmony_ci			dev_err(wled->dev, "invalid IO resources\n");
14368c2ecf20Sopenharmony_ci			return -EINVAL;
14378c2ecf20Sopenharmony_ci		}
14388c2ecf20Sopenharmony_ci		wled->sink_addr = be32_to_cpu(*prop_addr);
14398c2ecf20Sopenharmony_ci		break;
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	case 5:
14428c2ecf20Sopenharmony_ci		u32_opts = wled5_opts;
14438c2ecf20Sopenharmony_ci		size = ARRAY_SIZE(wled5_opts);
14448c2ecf20Sopenharmony_ci		*cfg = wled5_config_defaults;
14458c2ecf20Sopenharmony_ci		wled->wled_set_brightness = wled5_set_brightness;
14468c2ecf20Sopenharmony_ci		wled->wled_sync_toggle = wled3_sync_toggle;
14478c2ecf20Sopenharmony_ci		wled->wled_cabc_config = wled5_cabc_config;
14488c2ecf20Sopenharmony_ci		wled->wled_ovp_delay = wled5_ovp_delay;
14498c2ecf20Sopenharmony_ci		wled->wled_auto_detection_required =
14508c2ecf20Sopenharmony_ci					wled5_auto_detection_required;
14518c2ecf20Sopenharmony_ci		wled->max_string_count = 4;
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci		prop_addr = of_get_address(dev->of_node, 1, NULL, NULL);
14548c2ecf20Sopenharmony_ci		if (!prop_addr) {
14558c2ecf20Sopenharmony_ci			dev_err(wled->dev, "invalid IO resources\n");
14568c2ecf20Sopenharmony_ci			return -EINVAL;
14578c2ecf20Sopenharmony_ci		}
14588c2ecf20Sopenharmony_ci		wled->sink_addr = be32_to_cpu(*prop_addr);
14598c2ecf20Sopenharmony_ci		break;
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci	default:
14628c2ecf20Sopenharmony_ci		dev_err(wled->dev, "Invalid WLED version\n");
14638c2ecf20Sopenharmony_ci		return -EINVAL;
14648c2ecf20Sopenharmony_ci	}
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	for (i = 0; i < size; ++i) {
14678c2ecf20Sopenharmony_ci		rc = of_property_read_u32(dev->of_node, u32_opts[i].name, &val);
14688c2ecf20Sopenharmony_ci		if (rc == -EINVAL) {
14698c2ecf20Sopenharmony_ci			continue;
14708c2ecf20Sopenharmony_ci		} else if (rc) {
14718c2ecf20Sopenharmony_ci			dev_err(dev, "error reading '%s'\n", u32_opts[i].name);
14728c2ecf20Sopenharmony_ci			return rc;
14738c2ecf20Sopenharmony_ci		}
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci		c = UINT_MAX;
14768c2ecf20Sopenharmony_ci		for (j = 0; c != val; j++) {
14778c2ecf20Sopenharmony_ci			c = wled_values(u32_opts[i].cfg, j);
14788c2ecf20Sopenharmony_ci			if (c == UINT_MAX) {
14798c2ecf20Sopenharmony_ci				dev_err(dev, "invalid value for '%s'\n",
14808c2ecf20Sopenharmony_ci					u32_opts[i].name);
14818c2ecf20Sopenharmony_ci				return -EINVAL;
14828c2ecf20Sopenharmony_ci			}
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci			if (c == val)
14858c2ecf20Sopenharmony_ci				break;
14868c2ecf20Sopenharmony_ci		}
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci		dev_dbg(dev, "'%s' = %u\n", u32_opts[i].name, c);
14898c2ecf20Sopenharmony_ci		*u32_opts[i].val_ptr = j;
14908c2ecf20Sopenharmony_ci	}
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(bool_opts); ++i) {
14938c2ecf20Sopenharmony_ci		if (of_property_read_bool(dev->of_node, bool_opts[i].name))
14948c2ecf20Sopenharmony_ci			*bool_opts[i].val_ptr = true;
14958c2ecf20Sopenharmony_ci	}
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci	string_len = of_property_count_elems_of_size(dev->of_node,
14988c2ecf20Sopenharmony_ci						     "qcom,enabled-strings",
14998c2ecf20Sopenharmony_ci						     sizeof(u32));
15008c2ecf20Sopenharmony_ci	if (string_len > 0) {
15018c2ecf20Sopenharmony_ci		if (string_len > wled->max_string_count) {
15028c2ecf20Sopenharmony_ci			dev_err(dev, "Cannot have more than %d strings\n",
15038c2ecf20Sopenharmony_ci				wled->max_string_count);
15048c2ecf20Sopenharmony_ci			return -EINVAL;
15058c2ecf20Sopenharmony_ci		}
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci		rc = of_property_read_u32_array(dev->of_node,
15088c2ecf20Sopenharmony_ci						"qcom,enabled-strings",
15098c2ecf20Sopenharmony_ci						wled->cfg.enabled_strings,
15108c2ecf20Sopenharmony_ci						string_len);
15118c2ecf20Sopenharmony_ci		if (rc) {
15128c2ecf20Sopenharmony_ci			dev_err(dev, "Failed to read %d elements from qcom,enabled-strings: %d\n",
15138c2ecf20Sopenharmony_ci				string_len, rc);
15148c2ecf20Sopenharmony_ci			return rc;
15158c2ecf20Sopenharmony_ci		}
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci		for (i = 0; i < string_len; ++i) {
15188c2ecf20Sopenharmony_ci			if (wled->cfg.enabled_strings[i] >= wled->max_string_count) {
15198c2ecf20Sopenharmony_ci				dev_err(dev,
15208c2ecf20Sopenharmony_ci					"qcom,enabled-strings index %d at %d is out of bounds\n",
15218c2ecf20Sopenharmony_ci					wled->cfg.enabled_strings[i], i);
15228c2ecf20Sopenharmony_ci				return -EINVAL;
15238c2ecf20Sopenharmony_ci			}
15248c2ecf20Sopenharmony_ci		}
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci		cfg->num_strings = string_len;
15278c2ecf20Sopenharmony_ci	}
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci	rc = of_property_read_u32(dev->of_node, "qcom,num-strings", &val);
15308c2ecf20Sopenharmony_ci	if (!rc) {
15318c2ecf20Sopenharmony_ci		if (val < 1 || val > wled->max_string_count) {
15328c2ecf20Sopenharmony_ci			dev_err(dev, "qcom,num-strings must be between 1 and %d\n",
15338c2ecf20Sopenharmony_ci				wled->max_string_count);
15348c2ecf20Sopenharmony_ci			return -EINVAL;
15358c2ecf20Sopenharmony_ci		}
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci		if (string_len > 0) {
15388c2ecf20Sopenharmony_ci			dev_warn(dev, "Only one of qcom,num-strings or qcom,enabled-strings"
15398c2ecf20Sopenharmony_ci				      " should be set\n");
15408c2ecf20Sopenharmony_ci			if (val > string_len) {
15418c2ecf20Sopenharmony_ci				dev_err(dev, "qcom,num-strings exceeds qcom,enabled-strings\n");
15428c2ecf20Sopenharmony_ci				return -EINVAL;
15438c2ecf20Sopenharmony_ci			}
15448c2ecf20Sopenharmony_ci		}
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci		cfg->num_strings = val;
15478c2ecf20Sopenharmony_ci	}
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci	return 0;
15508c2ecf20Sopenharmony_ci}
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_cistatic int wled_configure_short_irq(struct wled *wled,
15538c2ecf20Sopenharmony_ci				    struct platform_device *pdev)
15548c2ecf20Sopenharmony_ci{
15558c2ecf20Sopenharmony_ci	int rc;
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	if (!wled->has_short_detect)
15588c2ecf20Sopenharmony_ci		return 0;
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
15618c2ecf20Sopenharmony_ci				WLED4_CTRL_REG_SHORT_PROTECT,
15628c2ecf20Sopenharmony_ci				WLED4_CTRL_REG_SHORT_EN_MASK,
15638c2ecf20Sopenharmony_ci				WLED4_CTRL_REG_SHORT_EN_MASK);
15648c2ecf20Sopenharmony_ci	if (rc < 0)
15658c2ecf20Sopenharmony_ci		return rc;
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci	wled->short_irq = platform_get_irq_byname(pdev, "short");
15688c2ecf20Sopenharmony_ci	if (wled->short_irq < 0) {
15698c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "short irq is not used\n");
15708c2ecf20Sopenharmony_ci		return 0;
15718c2ecf20Sopenharmony_ci	}
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci	rc = devm_request_threaded_irq(wled->dev, wled->short_irq,
15748c2ecf20Sopenharmony_ci				       NULL, wled_short_irq_handler,
15758c2ecf20Sopenharmony_ci				       IRQF_ONESHOT,
15768c2ecf20Sopenharmony_ci				       "wled_short_irq", wled);
15778c2ecf20Sopenharmony_ci	if (rc < 0)
15788c2ecf20Sopenharmony_ci		dev_err(wled->dev, "Unable to request short_irq (err:%d)\n",
15798c2ecf20Sopenharmony_ci			rc);
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	return rc;
15828c2ecf20Sopenharmony_ci}
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_cistatic int wled_configure_ovp_irq(struct wled *wled,
15858c2ecf20Sopenharmony_ci				  struct platform_device *pdev)
15868c2ecf20Sopenharmony_ci{
15878c2ecf20Sopenharmony_ci	int rc;
15888c2ecf20Sopenharmony_ci	u32 val;
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_ci	wled->ovp_irq = platform_get_irq_byname(pdev, "ovp");
15918c2ecf20Sopenharmony_ci	if (wled->ovp_irq < 0) {
15928c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "OVP IRQ not found - disabling automatic string detection\n");
15938c2ecf20Sopenharmony_ci		return 0;
15948c2ecf20Sopenharmony_ci	}
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci	rc = devm_request_threaded_irq(wled->dev, wled->ovp_irq, NULL,
15978c2ecf20Sopenharmony_ci				       wled_ovp_irq_handler, IRQF_ONESHOT,
15988c2ecf20Sopenharmony_ci				       "wled_ovp_irq", wled);
15998c2ecf20Sopenharmony_ci	if (rc < 0) {
16008c2ecf20Sopenharmony_ci		dev_err(wled->dev, "Unable to request ovp_irq (err:%d)\n",
16018c2ecf20Sopenharmony_ci			rc);
16028c2ecf20Sopenharmony_ci		wled->ovp_irq = 0;
16038c2ecf20Sopenharmony_ci		return 0;
16048c2ecf20Sopenharmony_ci	}
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	rc = regmap_read(wled->regmap, wled->ctrl_addr +
16078c2ecf20Sopenharmony_ci			 WLED3_CTRL_REG_MOD_EN, &val);
16088c2ecf20Sopenharmony_ci	if (rc < 0)
16098c2ecf20Sopenharmony_ci		return rc;
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci	/* Keep OVP irq disabled until module is enabled */
16128c2ecf20Sopenharmony_ci	if (!(val & WLED3_CTRL_REG_MOD_EN_MASK))
16138c2ecf20Sopenharmony_ci		disable_irq(wled->ovp_irq);
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci	return 0;
16168c2ecf20Sopenharmony_ci}
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_cistatic const struct backlight_ops wled_ops = {
16198c2ecf20Sopenharmony_ci	.update_status = wled_update_status,
16208c2ecf20Sopenharmony_ci};
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_cistatic int wled_probe(struct platform_device *pdev)
16238c2ecf20Sopenharmony_ci{
16248c2ecf20Sopenharmony_ci	struct backlight_properties props;
16258c2ecf20Sopenharmony_ci	struct backlight_device *bl;
16268c2ecf20Sopenharmony_ci	struct wled *wled;
16278c2ecf20Sopenharmony_ci	struct regmap *regmap;
16288c2ecf20Sopenharmony_ci	u32 val;
16298c2ecf20Sopenharmony_ci	int rc;
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci	regmap = dev_get_regmap(pdev->dev.parent, NULL);
16328c2ecf20Sopenharmony_ci	if (!regmap) {
16338c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Unable to get regmap\n");
16348c2ecf20Sopenharmony_ci		return -EINVAL;
16358c2ecf20Sopenharmony_ci	}
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	wled = devm_kzalloc(&pdev->dev, sizeof(*wled), GFP_KERNEL);
16388c2ecf20Sopenharmony_ci	if (!wled)
16398c2ecf20Sopenharmony_ci		return -ENOMEM;
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci	wled->regmap = regmap;
16428c2ecf20Sopenharmony_ci	wled->dev = &pdev->dev;
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	wled->version = (uintptr_t)of_device_get_match_data(&pdev->dev);
16458c2ecf20Sopenharmony_ci	if (!wled->version) {
16468c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Unknown device version\n");
16478c2ecf20Sopenharmony_ci		return -ENODEV;
16488c2ecf20Sopenharmony_ci	}
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	mutex_init(&wled->lock);
16518c2ecf20Sopenharmony_ci	rc = wled_configure(wled);
16528c2ecf20Sopenharmony_ci	if (rc)
16538c2ecf20Sopenharmony_ci		return rc;
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_ci	val = WLED3_SINK_REG_BRIGHT_MAX;
16568c2ecf20Sopenharmony_ci	of_property_read_u32(pdev->dev.of_node, "max-brightness", &val);
16578c2ecf20Sopenharmony_ci	wled->max_brightness = val;
16588c2ecf20Sopenharmony_ci
16598c2ecf20Sopenharmony_ci	switch (wled->version) {
16608c2ecf20Sopenharmony_ci	case 3:
16618c2ecf20Sopenharmony_ci		wled->cfg.auto_detection_enabled = false;
16628c2ecf20Sopenharmony_ci		rc = wled3_setup(wled);
16638c2ecf20Sopenharmony_ci		if (rc) {
16648c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "wled3_setup failed\n");
16658c2ecf20Sopenharmony_ci			return rc;
16668c2ecf20Sopenharmony_ci		}
16678c2ecf20Sopenharmony_ci		break;
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci	case 4:
16708c2ecf20Sopenharmony_ci		wled->has_short_detect = true;
16718c2ecf20Sopenharmony_ci		rc = wled4_setup(wled);
16728c2ecf20Sopenharmony_ci		if (rc) {
16738c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "wled4_setup failed\n");
16748c2ecf20Sopenharmony_ci			return rc;
16758c2ecf20Sopenharmony_ci		}
16768c2ecf20Sopenharmony_ci		break;
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	case 5:
16798c2ecf20Sopenharmony_ci		wled->has_short_detect = true;
16808c2ecf20Sopenharmony_ci		if (wled->cfg.cabc_sel)
16818c2ecf20Sopenharmony_ci			wled->max_brightness = WLED5_SINK_REG_BRIGHT_MAX_12B;
16828c2ecf20Sopenharmony_ci
16838c2ecf20Sopenharmony_ci		rc = wled5_setup(wled);
16848c2ecf20Sopenharmony_ci		if (rc) {
16858c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "wled5_setup failed\n");
16868c2ecf20Sopenharmony_ci			return rc;
16878c2ecf20Sopenharmony_ci		}
16888c2ecf20Sopenharmony_ci		break;
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci	default:
16918c2ecf20Sopenharmony_ci		dev_err(wled->dev, "Invalid WLED version\n");
16928c2ecf20Sopenharmony_ci		break;
16938c2ecf20Sopenharmony_ci	}
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&wled->ovp_work, wled_ovp_work);
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci	rc = wled_configure_short_irq(wled, pdev);
16988c2ecf20Sopenharmony_ci	if (rc < 0)
16998c2ecf20Sopenharmony_ci		return rc;
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci	rc = wled_configure_ovp_irq(wled, pdev);
17028c2ecf20Sopenharmony_ci	if (rc < 0)
17038c2ecf20Sopenharmony_ci		return rc;
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_ci	val = WLED_DEFAULT_BRIGHTNESS;
17068c2ecf20Sopenharmony_ci	of_property_read_u32(pdev->dev.of_node, "default-brightness", &val);
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_ci	memset(&props, 0, sizeof(struct backlight_properties));
17098c2ecf20Sopenharmony_ci	props.type = BACKLIGHT_RAW;
17108c2ecf20Sopenharmony_ci	props.brightness = val;
17118c2ecf20Sopenharmony_ci	props.max_brightness = wled->max_brightness;
17128c2ecf20Sopenharmony_ci	bl = devm_backlight_device_register(&pdev->dev, wled->name,
17138c2ecf20Sopenharmony_ci					    &pdev->dev, wled,
17148c2ecf20Sopenharmony_ci					    &wled_ops, &props);
17158c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(bl);
17168c2ecf20Sopenharmony_ci};
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_cistatic int wled_remove(struct platform_device *pdev)
17198c2ecf20Sopenharmony_ci{
17208c2ecf20Sopenharmony_ci	struct wled *wled = dev_get_drvdata(&pdev->dev);
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci	mutex_destroy(&wled->lock);
17238c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&wled->ovp_work);
17248c2ecf20Sopenharmony_ci	disable_irq(wled->short_irq);
17258c2ecf20Sopenharmony_ci	disable_irq(wled->ovp_irq);
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci	return 0;
17288c2ecf20Sopenharmony_ci}
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_cistatic const struct of_device_id wled_match_table[] = {
17318c2ecf20Sopenharmony_ci	{ .compatible = "qcom,pm8941-wled", .data = (void *)3 },
17328c2ecf20Sopenharmony_ci	{ .compatible = "qcom,pmi8998-wled", .data = (void *)4 },
17338c2ecf20Sopenharmony_ci	{ .compatible = "qcom,pm660l-wled", .data = (void *)4 },
17348c2ecf20Sopenharmony_ci	{ .compatible = "qcom,pm8150l-wled", .data = (void *)5 },
17358c2ecf20Sopenharmony_ci	{}
17368c2ecf20Sopenharmony_ci};
17378c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, wled_match_table);
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_cistatic struct platform_driver wled_driver = {
17408c2ecf20Sopenharmony_ci	.probe = wled_probe,
17418c2ecf20Sopenharmony_ci	.remove = wled_remove,
17428c2ecf20Sopenharmony_ci	.driver	= {
17438c2ecf20Sopenharmony_ci		.name = "qcom,wled",
17448c2ecf20Sopenharmony_ci		.of_match_table	= wled_match_table,
17458c2ecf20Sopenharmony_ci	},
17468c2ecf20Sopenharmony_ci};
17478c2ecf20Sopenharmony_ci
17488c2ecf20Sopenharmony_cimodule_platform_driver(wled_driver);
17498c2ecf20Sopenharmony_ci
17508c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm WLED driver");
17518c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1752