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