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