162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Copyright (c) 2015, Sony Mobile Communications, AB. 362306a36Sopenharmony_ci */ 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/delay.h> 662306a36Sopenharmony_ci#include <linux/interrupt.h> 762306a36Sopenharmony_ci#include <linux/ktime.h> 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/backlight.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/of.h> 1262306a36Sopenharmony_ci#include <linux/of_address.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/regmap.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* From DT binding */ 1762306a36Sopenharmony_ci#define WLED_MAX_STRINGS 4 1862306a36Sopenharmony_ci#define MOD_A 0 1962306a36Sopenharmony_ci#define MOD_B 1 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define WLED_DEFAULT_BRIGHTNESS 2048 2262306a36Sopenharmony_ci#define WLED_SOFT_START_DLY_US 10000 2362306a36Sopenharmony_ci#define WLED3_SINK_REG_BRIGHT_MAX 0xFFF 2462306a36Sopenharmony_ci#define WLED5_SINK_REG_BRIGHT_MAX_12B 0xFFF 2562306a36Sopenharmony_ci#define WLED5_SINK_REG_BRIGHT_MAX_15B 0x7FFF 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* WLED3/WLED4 control registers */ 2862306a36Sopenharmony_ci#define WLED3_CTRL_REG_FAULT_STATUS 0x08 2962306a36Sopenharmony_ci#define WLED3_CTRL_REG_ILIM_FAULT_BIT BIT(0) 3062306a36Sopenharmony_ci#define WLED3_CTRL_REG_OVP_FAULT_BIT BIT(1) 3162306a36Sopenharmony_ci#define WLED4_CTRL_REG_SC_FAULT_BIT BIT(2) 3262306a36Sopenharmony_ci#define WLED5_CTRL_REG_OVP_PRE_ALARM_BIT BIT(4) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define WLED3_CTRL_REG_INT_RT_STS 0x10 3562306a36Sopenharmony_ci#define WLED3_CTRL_REG_OVP_FAULT_STATUS BIT(1) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define WLED3_CTRL_REG_MOD_EN 0x46 3862306a36Sopenharmony_ci#define WLED3_CTRL_REG_MOD_EN_MASK BIT(7) 3962306a36Sopenharmony_ci#define WLED3_CTRL_REG_MOD_EN_SHIFT 7 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define WLED3_CTRL_REG_FEEDBACK_CONTROL 0x48 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define WLED3_CTRL_REG_FREQ 0x4c 4462306a36Sopenharmony_ci#define WLED3_CTRL_REG_FREQ_MASK GENMASK(3, 0) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define WLED3_CTRL_REG_OVP 0x4d 4762306a36Sopenharmony_ci#define WLED3_CTRL_REG_OVP_MASK GENMASK(1, 0) 4862306a36Sopenharmony_ci#define WLED5_CTRL_REG_OVP_MASK GENMASK(3, 0) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define WLED3_CTRL_REG_ILIMIT 0x4e 5162306a36Sopenharmony_ci#define WLED3_CTRL_REG_ILIMIT_MASK GENMASK(2, 0) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* WLED3/WLED4 sink registers */ 5462306a36Sopenharmony_ci#define WLED3_SINK_REG_SYNC 0x47 5562306a36Sopenharmony_ci#define WLED3_SINK_REG_SYNC_CLEAR 0x00 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define WLED3_SINK_REG_CURR_SINK 0x4f 5862306a36Sopenharmony_ci#define WLED3_SINK_REG_CURR_SINK_MASK GENMASK(7, 5) 5962306a36Sopenharmony_ci#define WLED3_SINK_REG_CURR_SINK_SHFT 5 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* WLED3 specific per-'string' registers below */ 6262306a36Sopenharmony_ci#define WLED3_SINK_REG_BRIGHT(n) (0x40 + n) 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define WLED3_SINK_REG_STR_MOD_EN(n) (0x60 + (n * 0x10)) 6562306a36Sopenharmony_ci#define WLED3_SINK_REG_STR_MOD_MASK BIT(7) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define WLED3_SINK_REG_STR_FULL_SCALE_CURR(n) (0x62 + (n * 0x10)) 6862306a36Sopenharmony_ci#define WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK GENMASK(4, 0) 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define WLED3_SINK_REG_STR_MOD_SRC(n) (0x63 + (n * 0x10)) 7162306a36Sopenharmony_ci#define WLED3_SINK_REG_STR_MOD_SRC_MASK BIT(0) 7262306a36Sopenharmony_ci#define WLED3_SINK_REG_STR_MOD_SRC_INT 0x00 7362306a36Sopenharmony_ci#define WLED3_SINK_REG_STR_MOD_SRC_EXT 0x01 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#define WLED3_SINK_REG_STR_CABC(n) (0x66 + (n * 0x10)) 7662306a36Sopenharmony_ci#define WLED3_SINK_REG_STR_CABC_MASK BIT(7) 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* WLED4 specific control registers */ 7962306a36Sopenharmony_ci#define WLED4_CTRL_REG_SHORT_PROTECT 0x5e 8062306a36Sopenharmony_ci#define WLED4_CTRL_REG_SHORT_EN_MASK BIT(7) 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#define WLED4_CTRL_REG_SEC_ACCESS 0xd0 8362306a36Sopenharmony_ci#define WLED4_CTRL_REG_SEC_UNLOCK 0xa5 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#define WLED4_CTRL_REG_TEST1 0xe2 8662306a36Sopenharmony_ci#define WLED4_CTRL_REG_TEST1_EXT_FET_DTEST2 0x09 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* WLED4 specific sink registers */ 8962306a36Sopenharmony_ci#define WLED4_SINK_REG_CURR_SINK 0x46 9062306a36Sopenharmony_ci#define WLED4_SINK_REG_CURR_SINK_MASK GENMASK(7, 4) 9162306a36Sopenharmony_ci#define WLED4_SINK_REG_CURR_SINK_SHFT 4 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* WLED4 specific per-'string' registers below */ 9462306a36Sopenharmony_ci#define WLED4_SINK_REG_STR_MOD_EN(n) (0x50 + (n * 0x10)) 9562306a36Sopenharmony_ci#define WLED4_SINK_REG_STR_MOD_MASK BIT(7) 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define WLED4_SINK_REG_STR_FULL_SCALE_CURR(n) (0x52 + (n * 0x10)) 9862306a36Sopenharmony_ci#define WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK GENMASK(3, 0) 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define WLED4_SINK_REG_STR_MOD_SRC(n) (0x53 + (n * 0x10)) 10162306a36Sopenharmony_ci#define WLED4_SINK_REG_STR_MOD_SRC_MASK BIT(0) 10262306a36Sopenharmony_ci#define WLED4_SINK_REG_STR_MOD_SRC_INT 0x00 10362306a36Sopenharmony_ci#define WLED4_SINK_REG_STR_MOD_SRC_EXT 0x01 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci#define WLED4_SINK_REG_STR_CABC(n) (0x56 + (n * 0x10)) 10662306a36Sopenharmony_ci#define WLED4_SINK_REG_STR_CABC_MASK BIT(7) 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#define WLED4_SINK_REG_BRIGHT(n) (0x57 + (n * 0x10)) 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/* WLED5 specific control registers */ 11162306a36Sopenharmony_ci#define WLED5_CTRL_REG_OVP_INT_CTL 0x5f 11262306a36Sopenharmony_ci#define WLED5_CTRL_REG_OVP_INT_TIMER_MASK GENMASK(2, 0) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* WLED5 specific sink registers */ 11562306a36Sopenharmony_ci#define WLED5_SINK_REG_MOD_A_EN 0x50 11662306a36Sopenharmony_ci#define WLED5_SINK_REG_MOD_B_EN 0x60 11762306a36Sopenharmony_ci#define WLED5_SINK_REG_MOD_EN_MASK BIT(7) 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#define WLED5_SINK_REG_MOD_A_SRC_SEL 0x51 12062306a36Sopenharmony_ci#define WLED5_SINK_REG_MOD_B_SRC_SEL 0x61 12162306a36Sopenharmony_ci#define WLED5_SINK_REG_MOD_SRC_SEL_HIGH 0 12262306a36Sopenharmony_ci#define WLED5_SINK_REG_MOD_SRC_SEL_EXT 0x03 12362306a36Sopenharmony_ci#define WLED5_SINK_REG_MOD_SRC_SEL_MASK GENMASK(1, 0) 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci#define WLED5_SINK_REG_MOD_A_BRIGHTNESS_WIDTH_SEL 0x52 12662306a36Sopenharmony_ci#define WLED5_SINK_REG_MOD_B_BRIGHTNESS_WIDTH_SEL 0x62 12762306a36Sopenharmony_ci#define WLED5_SINK_REG_BRIGHTNESS_WIDTH_12B 0 12862306a36Sopenharmony_ci#define WLED5_SINK_REG_BRIGHTNESS_WIDTH_15B 1 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci#define WLED5_SINK_REG_MOD_A_BRIGHTNESS_LSB 0x53 13162306a36Sopenharmony_ci#define WLED5_SINK_REG_MOD_A_BRIGHTNESS_MSB 0x54 13262306a36Sopenharmony_ci#define WLED5_SINK_REG_MOD_B_BRIGHTNESS_LSB 0x63 13362306a36Sopenharmony_ci#define WLED5_SINK_REG_MOD_B_BRIGHTNESS_MSB 0x64 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci#define WLED5_SINK_REG_MOD_SYNC_BIT 0x65 13662306a36Sopenharmony_ci#define WLED5_SINK_REG_SYNC_MOD_A_BIT BIT(0) 13762306a36Sopenharmony_ci#define WLED5_SINK_REG_SYNC_MOD_B_BIT BIT(1) 13862306a36Sopenharmony_ci#define WLED5_SINK_REG_SYNC_MASK GENMASK(1, 0) 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/* WLED5 specific per-'string' registers below */ 14162306a36Sopenharmony_ci#define WLED5_SINK_REG_STR_FULL_SCALE_CURR(n) (0x72 + (n * 0x10)) 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci#define WLED5_SINK_REG_STR_SRC_SEL(n) (0x73 + (n * 0x10)) 14462306a36Sopenharmony_ci#define WLED5_SINK_REG_SRC_SEL_MOD_A 0 14562306a36Sopenharmony_ci#define WLED5_SINK_REG_SRC_SEL_MOD_B 1 14662306a36Sopenharmony_ci#define WLED5_SINK_REG_SRC_SEL_MASK GENMASK(1, 0) 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistruct wled_var_cfg { 14962306a36Sopenharmony_ci const u32 *values; 15062306a36Sopenharmony_ci u32 (*fn)(u32); 15162306a36Sopenharmony_ci int size; 15262306a36Sopenharmony_ci}; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistruct wled_u32_opts { 15562306a36Sopenharmony_ci const char *name; 15662306a36Sopenharmony_ci u32 *val_ptr; 15762306a36Sopenharmony_ci const struct wled_var_cfg *cfg; 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistruct wled_bool_opts { 16162306a36Sopenharmony_ci const char *name; 16262306a36Sopenharmony_ci bool *val_ptr; 16362306a36Sopenharmony_ci}; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistruct wled_config { 16662306a36Sopenharmony_ci u32 boost_i_limit; 16762306a36Sopenharmony_ci u32 ovp; 16862306a36Sopenharmony_ci u32 switch_freq; 16962306a36Sopenharmony_ci u32 num_strings; 17062306a36Sopenharmony_ci u32 string_i_limit; 17162306a36Sopenharmony_ci u32 enabled_strings[WLED_MAX_STRINGS]; 17262306a36Sopenharmony_ci u32 mod_sel; 17362306a36Sopenharmony_ci u32 cabc_sel; 17462306a36Sopenharmony_ci bool cs_out_en; 17562306a36Sopenharmony_ci bool ext_gen; 17662306a36Sopenharmony_ci bool cabc; 17762306a36Sopenharmony_ci bool external_pfet; 17862306a36Sopenharmony_ci bool auto_detection_enabled; 17962306a36Sopenharmony_ci}; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistruct wled { 18262306a36Sopenharmony_ci const char *name; 18362306a36Sopenharmony_ci struct device *dev; 18462306a36Sopenharmony_ci struct regmap *regmap; 18562306a36Sopenharmony_ci struct mutex lock; /* Lock to avoid race from thread irq handler */ 18662306a36Sopenharmony_ci ktime_t last_short_event; 18762306a36Sopenharmony_ci ktime_t start_ovp_fault_time; 18862306a36Sopenharmony_ci u16 ctrl_addr; 18962306a36Sopenharmony_ci u16 sink_addr; 19062306a36Sopenharmony_ci u16 max_string_count; 19162306a36Sopenharmony_ci u16 auto_detection_ovp_count; 19262306a36Sopenharmony_ci u32 brightness; 19362306a36Sopenharmony_ci u32 max_brightness; 19462306a36Sopenharmony_ci u32 short_count; 19562306a36Sopenharmony_ci u32 auto_detect_count; 19662306a36Sopenharmony_ci u32 version; 19762306a36Sopenharmony_ci bool disabled_by_short; 19862306a36Sopenharmony_ci bool has_short_detect; 19962306a36Sopenharmony_ci bool cabc_disabled; 20062306a36Sopenharmony_ci int short_irq; 20162306a36Sopenharmony_ci int ovp_irq; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci struct wled_config cfg; 20462306a36Sopenharmony_ci struct delayed_work ovp_work; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* Configures the brightness. Applicable for wled3, wled4 and wled5 */ 20762306a36Sopenharmony_ci int (*wled_set_brightness)(struct wled *wled, u16 brightness); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* Configures the cabc register. Applicable for wled4 and wled5 */ 21062306a36Sopenharmony_ci int (*wled_cabc_config)(struct wled *wled, bool enable); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* 21362306a36Sopenharmony_ci * Toggles the sync bit for the brightness update to take place. 21462306a36Sopenharmony_ci * Applicable for WLED3, WLED4 and WLED5. 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_ci int (*wled_sync_toggle)(struct wled *wled); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* 21962306a36Sopenharmony_ci * Time to wait before checking the OVP status after wled module enable. 22062306a36Sopenharmony_ci * Applicable for WLED4 and WLED5. 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci int (*wled_ovp_delay)(struct wled *wled); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* 22562306a36Sopenharmony_ci * Determines if the auto string detection is required. 22662306a36Sopenharmony_ci * Applicable for WLED4 and WLED5 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci bool (*wled_auto_detection_required)(struct wled *wled); 22962306a36Sopenharmony_ci}; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic int wled3_set_brightness(struct wled *wled, u16 brightness) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci int rc, i; 23462306a36Sopenharmony_ci __le16 v; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci v = cpu_to_le16(brightness & WLED3_SINK_REG_BRIGHT_MAX); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci for (i = 0; i < wled->cfg.num_strings; ++i) { 23962306a36Sopenharmony_ci rc = regmap_bulk_write(wled->regmap, wled->ctrl_addr + 24062306a36Sopenharmony_ci WLED3_SINK_REG_BRIGHT(wled->cfg.enabled_strings[i]), 24162306a36Sopenharmony_ci &v, sizeof(v)); 24262306a36Sopenharmony_ci if (rc < 0) 24362306a36Sopenharmony_ci return rc; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return 0; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic int wled4_set_brightness(struct wled *wled, u16 brightness) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci int rc, i; 25262306a36Sopenharmony_ci u16 low_limit = wled->max_brightness * 4 / 1000; 25362306a36Sopenharmony_ci __le16 v; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* WLED4's lower limit of operation is 0.4% */ 25662306a36Sopenharmony_ci if (brightness > 0 && brightness < low_limit) 25762306a36Sopenharmony_ci brightness = low_limit; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci v = cpu_to_le16(brightness & WLED3_SINK_REG_BRIGHT_MAX); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci for (i = 0; i < wled->cfg.num_strings; ++i) { 26262306a36Sopenharmony_ci rc = regmap_bulk_write(wled->regmap, wled->sink_addr + 26362306a36Sopenharmony_ci WLED4_SINK_REG_BRIGHT(wled->cfg.enabled_strings[i]), 26462306a36Sopenharmony_ci &v, sizeof(v)); 26562306a36Sopenharmony_ci if (rc < 0) 26662306a36Sopenharmony_ci return rc; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic int wled5_set_brightness(struct wled *wled, u16 brightness) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci int rc, offset; 27562306a36Sopenharmony_ci u16 low_limit = wled->max_brightness * 1 / 1000; 27662306a36Sopenharmony_ci __le16 v; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* WLED5's lower limit is 0.1% */ 27962306a36Sopenharmony_ci if (brightness < low_limit) 28062306a36Sopenharmony_ci brightness = low_limit; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci v = cpu_to_le16(brightness & WLED5_SINK_REG_BRIGHT_MAX_15B); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci offset = (wled->cfg.mod_sel == MOD_A) ? 28562306a36Sopenharmony_ci WLED5_SINK_REG_MOD_A_BRIGHTNESS_LSB : 28662306a36Sopenharmony_ci WLED5_SINK_REG_MOD_B_BRIGHTNESS_LSB; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci rc = regmap_bulk_write(wled->regmap, wled->sink_addr + offset, 28962306a36Sopenharmony_ci &v, sizeof(v)); 29062306a36Sopenharmony_ci return rc; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic void wled_ovp_work(struct work_struct *work) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct wled *wled = container_of(work, 29662306a36Sopenharmony_ci struct wled, ovp_work.work); 29762306a36Sopenharmony_ci enable_irq(wled->ovp_irq); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic int wled_module_enable(struct wled *wled, int val) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci int rc; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (wled->disabled_by_short) 30562306a36Sopenharmony_ci return -ENXIO; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + 30862306a36Sopenharmony_ci WLED3_CTRL_REG_MOD_EN, 30962306a36Sopenharmony_ci WLED3_CTRL_REG_MOD_EN_MASK, 31062306a36Sopenharmony_ci val << WLED3_CTRL_REG_MOD_EN_SHIFT); 31162306a36Sopenharmony_ci if (rc < 0) 31262306a36Sopenharmony_ci return rc; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (wled->ovp_irq > 0) { 31562306a36Sopenharmony_ci if (val) { 31662306a36Sopenharmony_ci /* 31762306a36Sopenharmony_ci * The hardware generates a storm of spurious OVP 31862306a36Sopenharmony_ci * interrupts during soft start operations. So defer 31962306a36Sopenharmony_ci * enabling the IRQ for 10ms to ensure that the 32062306a36Sopenharmony_ci * soft start is complete. 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_ci schedule_delayed_work(&wled->ovp_work, HZ / 100); 32362306a36Sopenharmony_ci } else { 32462306a36Sopenharmony_ci if (!cancel_delayed_work_sync(&wled->ovp_work)) 32562306a36Sopenharmony_ci disable_irq(wled->ovp_irq); 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return 0; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int wled3_sync_toggle(struct wled *wled) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci int rc; 33562306a36Sopenharmony_ci unsigned int mask = GENMASK(wled->max_string_count - 1, 0); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, 33862306a36Sopenharmony_ci wled->sink_addr + WLED3_SINK_REG_SYNC, 33962306a36Sopenharmony_ci mask, WLED3_SINK_REG_SYNC_CLEAR); 34062306a36Sopenharmony_ci if (rc < 0) 34162306a36Sopenharmony_ci return rc; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, 34462306a36Sopenharmony_ci wled->sink_addr + WLED3_SINK_REG_SYNC, 34562306a36Sopenharmony_ci mask, mask); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return rc; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic int wled5_mod_sync_toggle(struct wled *wled) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci int rc; 35362306a36Sopenharmony_ci u8 val; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, 35662306a36Sopenharmony_ci wled->sink_addr + WLED5_SINK_REG_MOD_SYNC_BIT, 35762306a36Sopenharmony_ci WLED5_SINK_REG_SYNC_MASK, 0); 35862306a36Sopenharmony_ci if (rc < 0) 35962306a36Sopenharmony_ci return rc; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci val = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_REG_SYNC_MOD_A_BIT : 36262306a36Sopenharmony_ci WLED5_SINK_REG_SYNC_MOD_B_BIT; 36362306a36Sopenharmony_ci return regmap_update_bits(wled->regmap, 36462306a36Sopenharmony_ci wled->sink_addr + WLED5_SINK_REG_MOD_SYNC_BIT, 36562306a36Sopenharmony_ci WLED5_SINK_REG_SYNC_MASK, val); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic int wled_ovp_fault_status(struct wled *wled, bool *fault_set) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci int rc; 37162306a36Sopenharmony_ci u32 int_rt_sts, fault_sts; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci *fault_set = false; 37462306a36Sopenharmony_ci rc = regmap_read(wled->regmap, 37562306a36Sopenharmony_ci wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS, 37662306a36Sopenharmony_ci &int_rt_sts); 37762306a36Sopenharmony_ci if (rc < 0) { 37862306a36Sopenharmony_ci dev_err(wled->dev, "Failed to read INT_RT_STS rc=%d\n", rc); 37962306a36Sopenharmony_ci return rc; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci rc = regmap_read(wled->regmap, 38362306a36Sopenharmony_ci wled->ctrl_addr + WLED3_CTRL_REG_FAULT_STATUS, 38462306a36Sopenharmony_ci &fault_sts); 38562306a36Sopenharmony_ci if (rc < 0) { 38662306a36Sopenharmony_ci dev_err(wled->dev, "Failed to read FAULT_STATUS rc=%d\n", rc); 38762306a36Sopenharmony_ci return rc; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (int_rt_sts & WLED3_CTRL_REG_OVP_FAULT_STATUS) 39162306a36Sopenharmony_ci *fault_set = true; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (wled->version == 4 && (fault_sts & WLED3_CTRL_REG_OVP_FAULT_BIT)) 39462306a36Sopenharmony_ci *fault_set = true; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (wled->version == 5 && (fault_sts & (WLED3_CTRL_REG_OVP_FAULT_BIT | 39762306a36Sopenharmony_ci WLED5_CTRL_REG_OVP_PRE_ALARM_BIT))) 39862306a36Sopenharmony_ci *fault_set = true; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (*fault_set) 40162306a36Sopenharmony_ci dev_dbg(wled->dev, "WLED OVP fault detected, int_rt_sts=0x%x fault_sts=0x%x\n", 40262306a36Sopenharmony_ci int_rt_sts, fault_sts); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci return rc; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic int wled4_ovp_delay(struct wled *wled) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci return WLED_SOFT_START_DLY_US; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic int wled5_ovp_delay(struct wled *wled) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci int rc, delay_us; 41562306a36Sopenharmony_ci u32 val; 41662306a36Sopenharmony_ci u8 ovp_timer_ms[8] = {1, 2, 4, 8, 12, 16, 20, 24}; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* For WLED5, get the delay based on OVP timer */ 41962306a36Sopenharmony_ci rc = regmap_read(wled->regmap, wled->ctrl_addr + 42062306a36Sopenharmony_ci WLED5_CTRL_REG_OVP_INT_CTL, &val); 42162306a36Sopenharmony_ci if (rc < 0) 42262306a36Sopenharmony_ci delay_us = 42362306a36Sopenharmony_ci ovp_timer_ms[val & WLED5_CTRL_REG_OVP_INT_TIMER_MASK] * 1000; 42462306a36Sopenharmony_ci else 42562306a36Sopenharmony_ci delay_us = 2 * WLED_SOFT_START_DLY_US; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci dev_dbg(wled->dev, "delay_time_us: %d\n", delay_us); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci return delay_us; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic int wled_update_status(struct backlight_device *bl) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct wled *wled = bl_get_data(bl); 43562306a36Sopenharmony_ci u16 brightness = backlight_get_brightness(bl); 43662306a36Sopenharmony_ci int rc = 0; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci mutex_lock(&wled->lock); 43962306a36Sopenharmony_ci if (brightness) { 44062306a36Sopenharmony_ci rc = wled->wled_set_brightness(wled, brightness); 44162306a36Sopenharmony_ci if (rc < 0) { 44262306a36Sopenharmony_ci dev_err(wled->dev, "wled failed to set brightness rc:%d\n", 44362306a36Sopenharmony_ci rc); 44462306a36Sopenharmony_ci goto unlock_mutex; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (wled->version < 5) { 44862306a36Sopenharmony_ci rc = wled->wled_sync_toggle(wled); 44962306a36Sopenharmony_ci if (rc < 0) { 45062306a36Sopenharmony_ci dev_err(wled->dev, "wled sync failed rc:%d\n", rc); 45162306a36Sopenharmony_ci goto unlock_mutex; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci } else { 45462306a36Sopenharmony_ci /* 45562306a36Sopenharmony_ci * For WLED5 toggling the MOD_SYNC_BIT updates the 45662306a36Sopenharmony_ci * brightness 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_ci rc = wled5_mod_sync_toggle(wled); 45962306a36Sopenharmony_ci if (rc < 0) { 46062306a36Sopenharmony_ci dev_err(wled->dev, "wled mod sync failed rc:%d\n", 46162306a36Sopenharmony_ci rc); 46262306a36Sopenharmony_ci goto unlock_mutex; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (!!brightness != !!wled->brightness) { 46862306a36Sopenharmony_ci rc = wled_module_enable(wled, !!brightness); 46962306a36Sopenharmony_ci if (rc < 0) { 47062306a36Sopenharmony_ci dev_err(wled->dev, "wled enable failed rc:%d\n", rc); 47162306a36Sopenharmony_ci goto unlock_mutex; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci wled->brightness = brightness; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ciunlock_mutex: 47862306a36Sopenharmony_ci mutex_unlock(&wled->lock); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci return rc; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic int wled4_cabc_config(struct wled *wled, bool enable) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci int i, j, rc; 48662306a36Sopenharmony_ci u8 val; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci for (i = 0; i < wled->cfg.num_strings; i++) { 48962306a36Sopenharmony_ci j = wled->cfg.enabled_strings[i]; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci val = enable ? WLED4_SINK_REG_STR_CABC_MASK : 0; 49262306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, wled->sink_addr + 49362306a36Sopenharmony_ci WLED4_SINK_REG_STR_CABC(j), 49462306a36Sopenharmony_ci WLED4_SINK_REG_STR_CABC_MASK, val); 49562306a36Sopenharmony_ci if (rc < 0) 49662306a36Sopenharmony_ci return rc; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci return 0; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic int wled5_cabc_config(struct wled *wled, bool enable) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci int rc, offset; 50562306a36Sopenharmony_ci u8 reg; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (wled->cabc_disabled) 50862306a36Sopenharmony_ci return 0; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci reg = enable ? wled->cfg.cabc_sel : 0; 51162306a36Sopenharmony_ci offset = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_REG_MOD_A_SRC_SEL : 51262306a36Sopenharmony_ci WLED5_SINK_REG_MOD_B_SRC_SEL; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, wled->sink_addr + offset, 51562306a36Sopenharmony_ci WLED5_SINK_REG_MOD_SRC_SEL_MASK, reg); 51662306a36Sopenharmony_ci if (rc < 0) { 51762306a36Sopenharmony_ci pr_err("Error in configuring CABC rc=%d\n", rc); 51862306a36Sopenharmony_ci return rc; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (!wled->cfg.cabc_sel) 52262306a36Sopenharmony_ci wled->cabc_disabled = true; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return 0; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci#define WLED_SHORT_DLY_MS 20 52862306a36Sopenharmony_ci#define WLED_SHORT_CNT_MAX 5 52962306a36Sopenharmony_ci#define WLED_SHORT_RESET_CNT_DLY_US USEC_PER_SEC 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic irqreturn_t wled_short_irq_handler(int irq, void *_wled) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci struct wled *wled = _wled; 53462306a36Sopenharmony_ci int rc; 53562306a36Sopenharmony_ci s64 elapsed_time; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci wled->short_count++; 53862306a36Sopenharmony_ci mutex_lock(&wled->lock); 53962306a36Sopenharmony_ci rc = wled_module_enable(wled, false); 54062306a36Sopenharmony_ci if (rc < 0) { 54162306a36Sopenharmony_ci dev_err(wled->dev, "wled disable failed rc:%d\n", rc); 54262306a36Sopenharmony_ci goto unlock_mutex; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci elapsed_time = ktime_us_delta(ktime_get(), 54662306a36Sopenharmony_ci wled->last_short_event); 54762306a36Sopenharmony_ci if (elapsed_time > WLED_SHORT_RESET_CNT_DLY_US) 54862306a36Sopenharmony_ci wled->short_count = 1; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (wled->short_count > WLED_SHORT_CNT_MAX) { 55162306a36Sopenharmony_ci dev_err(wled->dev, "Short triggered %d times, disabling WLED forever!\n", 55262306a36Sopenharmony_ci wled->short_count); 55362306a36Sopenharmony_ci wled->disabled_by_short = true; 55462306a36Sopenharmony_ci goto unlock_mutex; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci wled->last_short_event = ktime_get(); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci msleep(WLED_SHORT_DLY_MS); 56062306a36Sopenharmony_ci rc = wled_module_enable(wled, true); 56162306a36Sopenharmony_ci if (rc < 0) 56262306a36Sopenharmony_ci dev_err(wled->dev, "wled enable failed rc:%d\n", rc); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ciunlock_mutex: 56562306a36Sopenharmony_ci mutex_unlock(&wled->lock); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci return IRQ_HANDLED; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci#define AUTO_DETECT_BRIGHTNESS 200 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic void wled_auto_string_detection(struct wled *wled) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci int rc = 0, i, j, delay_time_us; 57562306a36Sopenharmony_ci u32 sink_config = 0; 57662306a36Sopenharmony_ci u8 sink_test = 0, sink_valid = 0, val; 57762306a36Sopenharmony_ci bool fault_set; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* Read configured sink configuration */ 58062306a36Sopenharmony_ci rc = regmap_read(wled->regmap, wled->sink_addr + 58162306a36Sopenharmony_ci WLED4_SINK_REG_CURR_SINK, &sink_config); 58262306a36Sopenharmony_ci if (rc < 0) { 58362306a36Sopenharmony_ci dev_err(wled->dev, "Failed to read SINK configuration rc=%d\n", 58462306a36Sopenharmony_ci rc); 58562306a36Sopenharmony_ci goto failed_detect; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* Disable the module before starting detection */ 58962306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, 59062306a36Sopenharmony_ci wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN, 59162306a36Sopenharmony_ci WLED3_CTRL_REG_MOD_EN_MASK, 0); 59262306a36Sopenharmony_ci if (rc < 0) { 59362306a36Sopenharmony_ci dev_err(wled->dev, "Failed to disable WLED module rc=%d\n", rc); 59462306a36Sopenharmony_ci goto failed_detect; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* Set low brightness across all sinks */ 59862306a36Sopenharmony_ci rc = wled4_set_brightness(wled, AUTO_DETECT_BRIGHTNESS); 59962306a36Sopenharmony_ci if (rc < 0) { 60062306a36Sopenharmony_ci dev_err(wled->dev, "Failed to set brightness for auto detection rc=%d\n", 60162306a36Sopenharmony_ci rc); 60262306a36Sopenharmony_ci goto failed_detect; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if (wled->cfg.cabc) { 60662306a36Sopenharmony_ci rc = wled->wled_cabc_config(wled, false); 60762306a36Sopenharmony_ci if (rc < 0) 60862306a36Sopenharmony_ci goto failed_detect; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci /* Disable all sinks */ 61262306a36Sopenharmony_ci rc = regmap_write(wled->regmap, 61362306a36Sopenharmony_ci wled->sink_addr + WLED4_SINK_REG_CURR_SINK, 0); 61462306a36Sopenharmony_ci if (rc < 0) { 61562306a36Sopenharmony_ci dev_err(wled->dev, "Failed to disable all sinks rc=%d\n", rc); 61662306a36Sopenharmony_ci goto failed_detect; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci /* Iterate through the strings one by one */ 62062306a36Sopenharmony_ci for (i = 0; i < wled->cfg.num_strings; i++) { 62162306a36Sopenharmony_ci j = wled->cfg.enabled_strings[i]; 62262306a36Sopenharmony_ci sink_test = BIT((WLED4_SINK_REG_CURR_SINK_SHFT + j)); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* Enable feedback control */ 62562306a36Sopenharmony_ci rc = regmap_write(wled->regmap, wled->ctrl_addr + 62662306a36Sopenharmony_ci WLED3_CTRL_REG_FEEDBACK_CONTROL, j + 1); 62762306a36Sopenharmony_ci if (rc < 0) { 62862306a36Sopenharmony_ci dev_err(wled->dev, "Failed to enable feedback for SINK %d rc = %d\n", 62962306a36Sopenharmony_ci j + 1, rc); 63062306a36Sopenharmony_ci goto failed_detect; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* Enable the sink */ 63462306a36Sopenharmony_ci rc = regmap_write(wled->regmap, wled->sink_addr + 63562306a36Sopenharmony_ci WLED4_SINK_REG_CURR_SINK, sink_test); 63662306a36Sopenharmony_ci if (rc < 0) { 63762306a36Sopenharmony_ci dev_err(wled->dev, "Failed to configure SINK %d rc=%d\n", 63862306a36Sopenharmony_ci j + 1, rc); 63962306a36Sopenharmony_ci goto failed_detect; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci /* Enable the module */ 64362306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + 64462306a36Sopenharmony_ci WLED3_CTRL_REG_MOD_EN, 64562306a36Sopenharmony_ci WLED3_CTRL_REG_MOD_EN_MASK, 64662306a36Sopenharmony_ci WLED3_CTRL_REG_MOD_EN_MASK); 64762306a36Sopenharmony_ci if (rc < 0) { 64862306a36Sopenharmony_ci dev_err(wled->dev, "Failed to enable WLED module rc=%d\n", 64962306a36Sopenharmony_ci rc); 65062306a36Sopenharmony_ci goto failed_detect; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci delay_time_us = wled->wled_ovp_delay(wled); 65462306a36Sopenharmony_ci usleep_range(delay_time_us, delay_time_us + 1000); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci rc = wled_ovp_fault_status(wled, &fault_set); 65762306a36Sopenharmony_ci if (rc < 0) { 65862306a36Sopenharmony_ci dev_err(wled->dev, "Error in getting OVP fault_sts, rc=%d\n", 65962306a36Sopenharmony_ci rc); 66062306a36Sopenharmony_ci goto failed_detect; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (fault_set) 66462306a36Sopenharmony_ci dev_dbg(wled->dev, "WLED OVP fault detected with SINK %d\n", 66562306a36Sopenharmony_ci j + 1); 66662306a36Sopenharmony_ci else 66762306a36Sopenharmony_ci sink_valid |= sink_test; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci /* Disable the module */ 67062306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, 67162306a36Sopenharmony_ci wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN, 67262306a36Sopenharmony_ci WLED3_CTRL_REG_MOD_EN_MASK, 0); 67362306a36Sopenharmony_ci if (rc < 0) { 67462306a36Sopenharmony_ci dev_err(wled->dev, "Failed to disable WLED module rc=%d\n", 67562306a36Sopenharmony_ci rc); 67662306a36Sopenharmony_ci goto failed_detect; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci if (!sink_valid) { 68162306a36Sopenharmony_ci dev_err(wled->dev, "No valid WLED sinks found\n"); 68262306a36Sopenharmony_ci wled->disabled_by_short = true; 68362306a36Sopenharmony_ci goto failed_detect; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (sink_valid != sink_config) { 68762306a36Sopenharmony_ci dev_warn(wled->dev, "%x is not a valid sink configuration - using %x instead\n", 68862306a36Sopenharmony_ci sink_config, sink_valid); 68962306a36Sopenharmony_ci sink_config = sink_valid; 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci /* Write the new sink configuration */ 69362306a36Sopenharmony_ci rc = regmap_write(wled->regmap, 69462306a36Sopenharmony_ci wled->sink_addr + WLED4_SINK_REG_CURR_SINK, 69562306a36Sopenharmony_ci sink_config); 69662306a36Sopenharmony_ci if (rc < 0) { 69762306a36Sopenharmony_ci dev_err(wled->dev, "Failed to reconfigure the default sink rc=%d\n", 69862306a36Sopenharmony_ci rc); 69962306a36Sopenharmony_ci goto failed_detect; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* Enable valid sinks */ 70362306a36Sopenharmony_ci if (wled->version == 4) { 70462306a36Sopenharmony_ci for (i = 0; i < wled->cfg.num_strings; i++) { 70562306a36Sopenharmony_ci j = wled->cfg.enabled_strings[i]; 70662306a36Sopenharmony_ci if (sink_config & 70762306a36Sopenharmony_ci BIT(WLED4_SINK_REG_CURR_SINK_SHFT + j)) 70862306a36Sopenharmony_ci val = WLED4_SINK_REG_STR_MOD_MASK; 70962306a36Sopenharmony_ci else 71062306a36Sopenharmony_ci /* Disable modulator_en for unused sink */ 71162306a36Sopenharmony_ci val = 0; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci rc = regmap_write(wled->regmap, wled->sink_addr + 71462306a36Sopenharmony_ci WLED4_SINK_REG_STR_MOD_EN(j), val); 71562306a36Sopenharmony_ci if (rc < 0) { 71662306a36Sopenharmony_ci dev_err(wled->dev, "Failed to configure MODULATOR_EN rc=%d\n", 71762306a36Sopenharmony_ci rc); 71862306a36Sopenharmony_ci goto failed_detect; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* Enable CABC */ 72462306a36Sopenharmony_ci rc = wled->wled_cabc_config(wled, true); 72562306a36Sopenharmony_ci if (rc < 0) 72662306a36Sopenharmony_ci goto failed_detect; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* Restore the feedback setting */ 72962306a36Sopenharmony_ci rc = regmap_write(wled->regmap, 73062306a36Sopenharmony_ci wled->ctrl_addr + WLED3_CTRL_REG_FEEDBACK_CONTROL, 0); 73162306a36Sopenharmony_ci if (rc < 0) { 73262306a36Sopenharmony_ci dev_err(wled->dev, "Failed to restore feedback setting rc=%d\n", 73362306a36Sopenharmony_ci rc); 73462306a36Sopenharmony_ci goto failed_detect; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci /* Restore brightness */ 73862306a36Sopenharmony_ci rc = wled4_set_brightness(wled, wled->brightness); 73962306a36Sopenharmony_ci if (rc < 0) { 74062306a36Sopenharmony_ci dev_err(wled->dev, "Failed to set brightness after auto detection rc=%d\n", 74162306a36Sopenharmony_ci rc); 74262306a36Sopenharmony_ci goto failed_detect; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, 74662306a36Sopenharmony_ci wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN, 74762306a36Sopenharmony_ci WLED3_CTRL_REG_MOD_EN_MASK, 74862306a36Sopenharmony_ci WLED3_CTRL_REG_MOD_EN_MASK); 74962306a36Sopenharmony_ci if (rc < 0) { 75062306a36Sopenharmony_ci dev_err(wled->dev, "Failed to enable WLED module rc=%d\n", rc); 75162306a36Sopenharmony_ci goto failed_detect; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cifailed_detect: 75562306a36Sopenharmony_ci return; 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci#define WLED_AUTO_DETECT_OVP_COUNT 5 75962306a36Sopenharmony_ci#define WLED_AUTO_DETECT_CNT_DLY_US USEC_PER_SEC 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic bool wled4_auto_detection_required(struct wled *wled) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci s64 elapsed_time_us; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci if (!wled->cfg.auto_detection_enabled) 76662306a36Sopenharmony_ci return false; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci /* 76962306a36Sopenharmony_ci * Check if the OVP fault was an occasional one 77062306a36Sopenharmony_ci * or if it's firing continuously, the latter qualifies 77162306a36Sopenharmony_ci * for an auto-detection check. 77262306a36Sopenharmony_ci */ 77362306a36Sopenharmony_ci if (!wled->auto_detection_ovp_count) { 77462306a36Sopenharmony_ci wled->start_ovp_fault_time = ktime_get(); 77562306a36Sopenharmony_ci wled->auto_detection_ovp_count++; 77662306a36Sopenharmony_ci } else { 77762306a36Sopenharmony_ci elapsed_time_us = ktime_us_delta(ktime_get(), 77862306a36Sopenharmony_ci wled->start_ovp_fault_time); 77962306a36Sopenharmony_ci if (elapsed_time_us > WLED_AUTO_DETECT_CNT_DLY_US) 78062306a36Sopenharmony_ci wled->auto_detection_ovp_count = 0; 78162306a36Sopenharmony_ci else 78262306a36Sopenharmony_ci wled->auto_detection_ovp_count++; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (wled->auto_detection_ovp_count >= 78562306a36Sopenharmony_ci WLED_AUTO_DETECT_OVP_COUNT) { 78662306a36Sopenharmony_ci wled->auto_detection_ovp_count = 0; 78762306a36Sopenharmony_ci return true; 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci return false; 79262306a36Sopenharmony_ci} 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cistatic bool wled5_auto_detection_required(struct wled *wled) 79562306a36Sopenharmony_ci{ 79662306a36Sopenharmony_ci if (!wled->cfg.auto_detection_enabled) 79762306a36Sopenharmony_ci return false; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* 80062306a36Sopenharmony_ci * Unlike WLED4, WLED5 has OVP fault density interrupt configuration 80162306a36Sopenharmony_ci * i.e. to count the number of OVP alarms for a certain duration before 80262306a36Sopenharmony_ci * triggering OVP fault interrupt. By default, number of OVP fault 80362306a36Sopenharmony_ci * events counted before an interrupt is fired is 32 and the time 80462306a36Sopenharmony_ci * interval is 12 ms. If we see one OVP fault interrupt, then that 80562306a36Sopenharmony_ci * should qualify for a real OVP fault condition to run auto detection 80662306a36Sopenharmony_ci * algorithm. 80762306a36Sopenharmony_ci */ 80862306a36Sopenharmony_ci return true; 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistatic int wled_auto_detection_at_init(struct wled *wled) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci int rc; 81462306a36Sopenharmony_ci bool fault_set; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (!wled->cfg.auto_detection_enabled) 81762306a36Sopenharmony_ci return 0; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci rc = wled_ovp_fault_status(wled, &fault_set); 82062306a36Sopenharmony_ci if (rc < 0) { 82162306a36Sopenharmony_ci dev_err(wled->dev, "Error in getting OVP fault_sts, rc=%d\n", 82262306a36Sopenharmony_ci rc); 82362306a36Sopenharmony_ci return rc; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (fault_set) { 82762306a36Sopenharmony_ci mutex_lock(&wled->lock); 82862306a36Sopenharmony_ci wled_auto_string_detection(wled); 82962306a36Sopenharmony_ci mutex_unlock(&wled->lock); 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci return rc; 83362306a36Sopenharmony_ci} 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_cistatic irqreturn_t wled_ovp_irq_handler(int irq, void *_wled) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci struct wled *wled = _wled; 83862306a36Sopenharmony_ci int rc; 83962306a36Sopenharmony_ci u32 int_sts, fault_sts; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci rc = regmap_read(wled->regmap, 84262306a36Sopenharmony_ci wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS, &int_sts); 84362306a36Sopenharmony_ci if (rc < 0) { 84462306a36Sopenharmony_ci dev_err(wled->dev, "Error in reading WLED3_INT_RT_STS rc=%d\n", 84562306a36Sopenharmony_ci rc); 84662306a36Sopenharmony_ci return IRQ_HANDLED; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci rc = regmap_read(wled->regmap, wled->ctrl_addr + 85062306a36Sopenharmony_ci WLED3_CTRL_REG_FAULT_STATUS, &fault_sts); 85162306a36Sopenharmony_ci if (rc < 0) { 85262306a36Sopenharmony_ci dev_err(wled->dev, "Error in reading WLED_FAULT_STATUS rc=%d\n", 85362306a36Sopenharmony_ci rc); 85462306a36Sopenharmony_ci return IRQ_HANDLED; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci if (fault_sts & (WLED3_CTRL_REG_OVP_FAULT_BIT | 85862306a36Sopenharmony_ci WLED3_CTRL_REG_ILIM_FAULT_BIT)) 85962306a36Sopenharmony_ci dev_dbg(wled->dev, "WLED OVP fault detected, int_sts=%x fault_sts= %x\n", 86062306a36Sopenharmony_ci int_sts, fault_sts); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci if (fault_sts & WLED3_CTRL_REG_OVP_FAULT_BIT) { 86362306a36Sopenharmony_ci if (wled->wled_auto_detection_required(wled)) { 86462306a36Sopenharmony_ci mutex_lock(&wled->lock); 86562306a36Sopenharmony_ci wled_auto_string_detection(wled); 86662306a36Sopenharmony_ci mutex_unlock(&wled->lock); 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci return IRQ_HANDLED; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic int wled3_setup(struct wled *wled) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci u16 addr; 87662306a36Sopenharmony_ci u8 sink_en = 0; 87762306a36Sopenharmony_ci int rc, i, j; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, 88062306a36Sopenharmony_ci wled->ctrl_addr + WLED3_CTRL_REG_OVP, 88162306a36Sopenharmony_ci WLED3_CTRL_REG_OVP_MASK, wled->cfg.ovp); 88262306a36Sopenharmony_ci if (rc) 88362306a36Sopenharmony_ci return rc; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, 88662306a36Sopenharmony_ci wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT, 88762306a36Sopenharmony_ci WLED3_CTRL_REG_ILIMIT_MASK, 88862306a36Sopenharmony_ci wled->cfg.boost_i_limit); 88962306a36Sopenharmony_ci if (rc) 89062306a36Sopenharmony_ci return rc; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, 89362306a36Sopenharmony_ci wled->ctrl_addr + WLED3_CTRL_REG_FREQ, 89462306a36Sopenharmony_ci WLED3_CTRL_REG_FREQ_MASK, 89562306a36Sopenharmony_ci wled->cfg.switch_freq); 89662306a36Sopenharmony_ci if (rc) 89762306a36Sopenharmony_ci return rc; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci for (i = 0; i < wled->cfg.num_strings; ++i) { 90062306a36Sopenharmony_ci j = wled->cfg.enabled_strings[i]; 90162306a36Sopenharmony_ci addr = wled->ctrl_addr + WLED3_SINK_REG_STR_MOD_EN(j); 90262306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, addr, 90362306a36Sopenharmony_ci WLED3_SINK_REG_STR_MOD_MASK, 90462306a36Sopenharmony_ci WLED3_SINK_REG_STR_MOD_MASK); 90562306a36Sopenharmony_ci if (rc) 90662306a36Sopenharmony_ci return rc; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci if (wled->cfg.ext_gen) { 90962306a36Sopenharmony_ci addr = wled->ctrl_addr + WLED3_SINK_REG_STR_MOD_SRC(j); 91062306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, addr, 91162306a36Sopenharmony_ci WLED3_SINK_REG_STR_MOD_SRC_MASK, 91262306a36Sopenharmony_ci WLED3_SINK_REG_STR_MOD_SRC_EXT); 91362306a36Sopenharmony_ci if (rc) 91462306a36Sopenharmony_ci return rc; 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci addr = wled->ctrl_addr + WLED3_SINK_REG_STR_FULL_SCALE_CURR(j); 91862306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, addr, 91962306a36Sopenharmony_ci WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK, 92062306a36Sopenharmony_ci wled->cfg.string_i_limit); 92162306a36Sopenharmony_ci if (rc) 92262306a36Sopenharmony_ci return rc; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci addr = wled->ctrl_addr + WLED3_SINK_REG_STR_CABC(j); 92562306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, addr, 92662306a36Sopenharmony_ci WLED3_SINK_REG_STR_CABC_MASK, 92762306a36Sopenharmony_ci wled->cfg.cabc ? 92862306a36Sopenharmony_ci WLED3_SINK_REG_STR_CABC_MASK : 0); 92962306a36Sopenharmony_ci if (rc) 93062306a36Sopenharmony_ci return rc; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci sink_en |= BIT(j + WLED3_SINK_REG_CURR_SINK_SHFT); 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, 93662306a36Sopenharmony_ci wled->ctrl_addr + WLED3_SINK_REG_CURR_SINK, 93762306a36Sopenharmony_ci WLED3_SINK_REG_CURR_SINK_MASK, sink_en); 93862306a36Sopenharmony_ci if (rc) 93962306a36Sopenharmony_ci return rc; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci return 0; 94262306a36Sopenharmony_ci} 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_cistatic const struct wled_config wled3_config_defaults = { 94562306a36Sopenharmony_ci .boost_i_limit = 3, 94662306a36Sopenharmony_ci .string_i_limit = 20, 94762306a36Sopenharmony_ci .ovp = 2, 94862306a36Sopenharmony_ci .num_strings = 3, 94962306a36Sopenharmony_ci .switch_freq = 5, 95062306a36Sopenharmony_ci .cs_out_en = false, 95162306a36Sopenharmony_ci .ext_gen = false, 95262306a36Sopenharmony_ci .cabc = false, 95362306a36Sopenharmony_ci .enabled_strings = {0, 1, 2}, 95462306a36Sopenharmony_ci}; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_cistatic int wled4_setup(struct wled *wled) 95762306a36Sopenharmony_ci{ 95862306a36Sopenharmony_ci int rc, temp, i, j; 95962306a36Sopenharmony_ci u16 addr; 96062306a36Sopenharmony_ci u8 sink_en = 0; 96162306a36Sopenharmony_ci u32 sink_cfg; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, 96462306a36Sopenharmony_ci wled->ctrl_addr + WLED3_CTRL_REG_OVP, 96562306a36Sopenharmony_ci WLED3_CTRL_REG_OVP_MASK, wled->cfg.ovp); 96662306a36Sopenharmony_ci if (rc < 0) 96762306a36Sopenharmony_ci return rc; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, 97062306a36Sopenharmony_ci wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT, 97162306a36Sopenharmony_ci WLED3_CTRL_REG_ILIMIT_MASK, 97262306a36Sopenharmony_ci wled->cfg.boost_i_limit); 97362306a36Sopenharmony_ci if (rc < 0) 97462306a36Sopenharmony_ci return rc; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, 97762306a36Sopenharmony_ci wled->ctrl_addr + WLED3_CTRL_REG_FREQ, 97862306a36Sopenharmony_ci WLED3_CTRL_REG_FREQ_MASK, 97962306a36Sopenharmony_ci wled->cfg.switch_freq); 98062306a36Sopenharmony_ci if (rc < 0) 98162306a36Sopenharmony_ci return rc; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci if (wled->cfg.external_pfet) { 98462306a36Sopenharmony_ci /* Unlock the secure register access */ 98562306a36Sopenharmony_ci rc = regmap_write(wled->regmap, wled->ctrl_addr + 98662306a36Sopenharmony_ci WLED4_CTRL_REG_SEC_ACCESS, 98762306a36Sopenharmony_ci WLED4_CTRL_REG_SEC_UNLOCK); 98862306a36Sopenharmony_ci if (rc < 0) 98962306a36Sopenharmony_ci return rc; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci rc = regmap_write(wled->regmap, 99262306a36Sopenharmony_ci wled->ctrl_addr + WLED4_CTRL_REG_TEST1, 99362306a36Sopenharmony_ci WLED4_CTRL_REG_TEST1_EXT_FET_DTEST2); 99462306a36Sopenharmony_ci if (rc < 0) 99562306a36Sopenharmony_ci return rc; 99662306a36Sopenharmony_ci } 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci rc = regmap_read(wled->regmap, wled->sink_addr + 99962306a36Sopenharmony_ci WLED4_SINK_REG_CURR_SINK, &sink_cfg); 100062306a36Sopenharmony_ci if (rc < 0) 100162306a36Sopenharmony_ci return rc; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci for (i = 0; i < wled->cfg.num_strings; i++) { 100462306a36Sopenharmony_ci j = wled->cfg.enabled_strings[i]; 100562306a36Sopenharmony_ci temp = j + WLED4_SINK_REG_CURR_SINK_SHFT; 100662306a36Sopenharmony_ci sink_en |= 1 << temp; 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci if (sink_cfg == sink_en) { 101062306a36Sopenharmony_ci rc = wled_auto_detection_at_init(wled); 101162306a36Sopenharmony_ci return rc; 101262306a36Sopenharmony_ci } 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, 101562306a36Sopenharmony_ci wled->sink_addr + WLED4_SINK_REG_CURR_SINK, 101662306a36Sopenharmony_ci WLED4_SINK_REG_CURR_SINK_MASK, 0); 101762306a36Sopenharmony_ci if (rc < 0) 101862306a36Sopenharmony_ci return rc; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + 102162306a36Sopenharmony_ci WLED3_CTRL_REG_MOD_EN, 102262306a36Sopenharmony_ci WLED3_CTRL_REG_MOD_EN_MASK, 0); 102362306a36Sopenharmony_ci if (rc < 0) 102462306a36Sopenharmony_ci return rc; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci /* Per sink/string configuration */ 102762306a36Sopenharmony_ci for (i = 0; i < wled->cfg.num_strings; i++) { 102862306a36Sopenharmony_ci j = wled->cfg.enabled_strings[i]; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci addr = wled->sink_addr + 103162306a36Sopenharmony_ci WLED4_SINK_REG_STR_MOD_EN(j); 103262306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, addr, 103362306a36Sopenharmony_ci WLED4_SINK_REG_STR_MOD_MASK, 103462306a36Sopenharmony_ci WLED4_SINK_REG_STR_MOD_MASK); 103562306a36Sopenharmony_ci if (rc < 0) 103662306a36Sopenharmony_ci return rc; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci addr = wled->sink_addr + 103962306a36Sopenharmony_ci WLED4_SINK_REG_STR_FULL_SCALE_CURR(j); 104062306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, addr, 104162306a36Sopenharmony_ci WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK, 104262306a36Sopenharmony_ci wled->cfg.string_i_limit); 104362306a36Sopenharmony_ci if (rc < 0) 104462306a36Sopenharmony_ci return rc; 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci rc = wled4_cabc_config(wled, wled->cfg.cabc); 104862306a36Sopenharmony_ci if (rc < 0) 104962306a36Sopenharmony_ci return rc; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + 105262306a36Sopenharmony_ci WLED3_CTRL_REG_MOD_EN, 105362306a36Sopenharmony_ci WLED3_CTRL_REG_MOD_EN_MASK, 105462306a36Sopenharmony_ci WLED3_CTRL_REG_MOD_EN_MASK); 105562306a36Sopenharmony_ci if (rc < 0) 105662306a36Sopenharmony_ci return rc; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, 105962306a36Sopenharmony_ci wled->sink_addr + WLED4_SINK_REG_CURR_SINK, 106062306a36Sopenharmony_ci WLED4_SINK_REG_CURR_SINK_MASK, sink_en); 106162306a36Sopenharmony_ci if (rc < 0) 106262306a36Sopenharmony_ci return rc; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci rc = wled->wled_sync_toggle(wled); 106562306a36Sopenharmony_ci if (rc < 0) { 106662306a36Sopenharmony_ci dev_err(wled->dev, "Failed to toggle sync reg rc:%d\n", rc); 106762306a36Sopenharmony_ci return rc; 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci rc = wled_auto_detection_at_init(wled); 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci return rc; 107362306a36Sopenharmony_ci} 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_cistatic const struct wled_config wled4_config_defaults = { 107662306a36Sopenharmony_ci .boost_i_limit = 4, 107762306a36Sopenharmony_ci .string_i_limit = 10, 107862306a36Sopenharmony_ci .ovp = 1, 107962306a36Sopenharmony_ci .num_strings = 4, 108062306a36Sopenharmony_ci .switch_freq = 11, 108162306a36Sopenharmony_ci .cabc = false, 108262306a36Sopenharmony_ci .external_pfet = false, 108362306a36Sopenharmony_ci .auto_detection_enabled = false, 108462306a36Sopenharmony_ci .enabled_strings = {0, 1, 2, 3}, 108562306a36Sopenharmony_ci}; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_cistatic int wled5_setup(struct wled *wled) 108862306a36Sopenharmony_ci{ 108962306a36Sopenharmony_ci int rc, temp, i, j, offset; 109062306a36Sopenharmony_ci u8 sink_en = 0; 109162306a36Sopenharmony_ci u16 addr; 109262306a36Sopenharmony_ci u32 val; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, 109562306a36Sopenharmony_ci wled->ctrl_addr + WLED3_CTRL_REG_OVP, 109662306a36Sopenharmony_ci WLED5_CTRL_REG_OVP_MASK, wled->cfg.ovp); 109762306a36Sopenharmony_ci if (rc < 0) 109862306a36Sopenharmony_ci return rc; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, 110162306a36Sopenharmony_ci wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT, 110262306a36Sopenharmony_ci WLED3_CTRL_REG_ILIMIT_MASK, 110362306a36Sopenharmony_ci wled->cfg.boost_i_limit); 110462306a36Sopenharmony_ci if (rc < 0) 110562306a36Sopenharmony_ci return rc; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, 110862306a36Sopenharmony_ci wled->ctrl_addr + WLED3_CTRL_REG_FREQ, 110962306a36Sopenharmony_ci WLED3_CTRL_REG_FREQ_MASK, 111062306a36Sopenharmony_ci wled->cfg.switch_freq); 111162306a36Sopenharmony_ci if (rc < 0) 111262306a36Sopenharmony_ci return rc; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci /* Per sink/string configuration */ 111562306a36Sopenharmony_ci for (i = 0; i < wled->cfg.num_strings; ++i) { 111662306a36Sopenharmony_ci j = wled->cfg.enabled_strings[i]; 111762306a36Sopenharmony_ci addr = wled->sink_addr + 111862306a36Sopenharmony_ci WLED4_SINK_REG_STR_FULL_SCALE_CURR(j); 111962306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, addr, 112062306a36Sopenharmony_ci WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK, 112162306a36Sopenharmony_ci wled->cfg.string_i_limit); 112262306a36Sopenharmony_ci if (rc < 0) 112362306a36Sopenharmony_ci return rc; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci addr = wled->sink_addr + WLED5_SINK_REG_STR_SRC_SEL(j); 112662306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, addr, 112762306a36Sopenharmony_ci WLED5_SINK_REG_SRC_SEL_MASK, 112862306a36Sopenharmony_ci wled->cfg.mod_sel == MOD_A ? 112962306a36Sopenharmony_ci WLED5_SINK_REG_SRC_SEL_MOD_A : 113062306a36Sopenharmony_ci WLED5_SINK_REG_SRC_SEL_MOD_B); 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci temp = j + WLED4_SINK_REG_CURR_SINK_SHFT; 113362306a36Sopenharmony_ci sink_en |= 1 << temp; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci rc = wled5_cabc_config(wled, wled->cfg.cabc_sel ? true : false); 113762306a36Sopenharmony_ci if (rc < 0) 113862306a36Sopenharmony_ci return rc; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci /* Enable one of the modulators A or B based on mod_sel */ 114162306a36Sopenharmony_ci addr = wled->sink_addr + WLED5_SINK_REG_MOD_A_EN; 114262306a36Sopenharmony_ci val = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_REG_MOD_EN_MASK : 0; 114362306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, addr, 114462306a36Sopenharmony_ci WLED5_SINK_REG_MOD_EN_MASK, val); 114562306a36Sopenharmony_ci if (rc < 0) 114662306a36Sopenharmony_ci return rc; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci addr = wled->sink_addr + WLED5_SINK_REG_MOD_B_EN; 114962306a36Sopenharmony_ci val = (wled->cfg.mod_sel == MOD_B) ? WLED5_SINK_REG_MOD_EN_MASK : 0; 115062306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, addr, 115162306a36Sopenharmony_ci WLED5_SINK_REG_MOD_EN_MASK, val); 115262306a36Sopenharmony_ci if (rc < 0) 115362306a36Sopenharmony_ci return rc; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci offset = (wled->cfg.mod_sel == MOD_A) ? 115662306a36Sopenharmony_ci WLED5_SINK_REG_MOD_A_BRIGHTNESS_WIDTH_SEL : 115762306a36Sopenharmony_ci WLED5_SINK_REG_MOD_B_BRIGHTNESS_WIDTH_SEL; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci addr = wled->sink_addr + offset; 116062306a36Sopenharmony_ci val = (wled->max_brightness == WLED5_SINK_REG_BRIGHT_MAX_15B) ? 116162306a36Sopenharmony_ci WLED5_SINK_REG_BRIGHTNESS_WIDTH_15B : 116262306a36Sopenharmony_ci WLED5_SINK_REG_BRIGHTNESS_WIDTH_12B; 116362306a36Sopenharmony_ci rc = regmap_write(wled->regmap, addr, val); 116462306a36Sopenharmony_ci if (rc < 0) 116562306a36Sopenharmony_ci return rc; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, 116862306a36Sopenharmony_ci wled->sink_addr + WLED4_SINK_REG_CURR_SINK, 116962306a36Sopenharmony_ci WLED4_SINK_REG_CURR_SINK_MASK, sink_en); 117062306a36Sopenharmony_ci if (rc < 0) 117162306a36Sopenharmony_ci return rc; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci /* This updates only FSC configuration in WLED5 */ 117462306a36Sopenharmony_ci rc = wled->wled_sync_toggle(wled); 117562306a36Sopenharmony_ci if (rc < 0) { 117662306a36Sopenharmony_ci pr_err("Failed to toggle sync reg rc:%d\n", rc); 117762306a36Sopenharmony_ci return rc; 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci rc = wled_auto_detection_at_init(wled); 118162306a36Sopenharmony_ci if (rc < 0) 118262306a36Sopenharmony_ci return rc; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci return 0; 118562306a36Sopenharmony_ci} 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_cistatic const struct wled_config wled5_config_defaults = { 118862306a36Sopenharmony_ci .boost_i_limit = 5, 118962306a36Sopenharmony_ci .string_i_limit = 10, 119062306a36Sopenharmony_ci .ovp = 4, 119162306a36Sopenharmony_ci .num_strings = 4, 119262306a36Sopenharmony_ci .switch_freq = 11, 119362306a36Sopenharmony_ci .mod_sel = 0, 119462306a36Sopenharmony_ci .cabc_sel = 0, 119562306a36Sopenharmony_ci .cabc = false, 119662306a36Sopenharmony_ci .external_pfet = false, 119762306a36Sopenharmony_ci .auto_detection_enabled = false, 119862306a36Sopenharmony_ci .enabled_strings = {0, 1, 2, 3}, 119962306a36Sopenharmony_ci}; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_cistatic const u32 wled3_boost_i_limit_values[] = { 120262306a36Sopenharmony_ci 105, 385, 525, 805, 980, 1260, 1400, 1680, 120362306a36Sopenharmony_ci}; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_cistatic const struct wled_var_cfg wled3_boost_i_limit_cfg = { 120662306a36Sopenharmony_ci .values = wled3_boost_i_limit_values, 120762306a36Sopenharmony_ci .size = ARRAY_SIZE(wled3_boost_i_limit_values), 120862306a36Sopenharmony_ci}; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_cistatic const u32 wled4_boost_i_limit_values[] = { 121162306a36Sopenharmony_ci 105, 280, 450, 620, 970, 1150, 1300, 1500, 121262306a36Sopenharmony_ci}; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_cistatic const struct wled_var_cfg wled4_boost_i_limit_cfg = { 121562306a36Sopenharmony_ci .values = wled4_boost_i_limit_values, 121662306a36Sopenharmony_ci .size = ARRAY_SIZE(wled4_boost_i_limit_values), 121762306a36Sopenharmony_ci}; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_cistatic inline u32 wled5_boost_i_limit_values_fn(u32 idx) 122062306a36Sopenharmony_ci{ 122162306a36Sopenharmony_ci return 525 + (idx * 175); 122262306a36Sopenharmony_ci} 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_cistatic const struct wled_var_cfg wled5_boost_i_limit_cfg = { 122562306a36Sopenharmony_ci .fn = wled5_boost_i_limit_values_fn, 122662306a36Sopenharmony_ci .size = 8, 122762306a36Sopenharmony_ci}; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_cistatic const u32 wled3_ovp_values[] = { 123062306a36Sopenharmony_ci 35, 32, 29, 27, 123162306a36Sopenharmony_ci}; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_cistatic const struct wled_var_cfg wled3_ovp_cfg = { 123462306a36Sopenharmony_ci .values = wled3_ovp_values, 123562306a36Sopenharmony_ci .size = ARRAY_SIZE(wled3_ovp_values), 123662306a36Sopenharmony_ci}; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_cistatic const u32 wled4_ovp_values[] = { 123962306a36Sopenharmony_ci 31100, 29600, 19600, 18100, 124062306a36Sopenharmony_ci}; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_cistatic const struct wled_var_cfg wled4_ovp_cfg = { 124362306a36Sopenharmony_ci .values = wled4_ovp_values, 124462306a36Sopenharmony_ci .size = ARRAY_SIZE(wled4_ovp_values), 124562306a36Sopenharmony_ci}; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_cistatic inline u32 wled5_ovp_values_fn(u32 idx) 124862306a36Sopenharmony_ci{ 124962306a36Sopenharmony_ci /* 125062306a36Sopenharmony_ci * 0000 - 38.5 V 125162306a36Sopenharmony_ci * 0001 - 37 V .. 125262306a36Sopenharmony_ci * 1111 - 16 V 125362306a36Sopenharmony_ci */ 125462306a36Sopenharmony_ci return 38500 - (idx * 1500); 125562306a36Sopenharmony_ci} 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_cistatic const struct wled_var_cfg wled5_ovp_cfg = { 125862306a36Sopenharmony_ci .fn = wled5_ovp_values_fn, 125962306a36Sopenharmony_ci .size = 16, 126062306a36Sopenharmony_ci}; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_cistatic u32 wled3_switch_freq_values_fn(u32 idx) 126362306a36Sopenharmony_ci{ 126462306a36Sopenharmony_ci return 19200 / (2 * (1 + idx)); 126562306a36Sopenharmony_ci} 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_cistatic const struct wled_var_cfg wled3_switch_freq_cfg = { 126862306a36Sopenharmony_ci .fn = wled3_switch_freq_values_fn, 126962306a36Sopenharmony_ci .size = 16, 127062306a36Sopenharmony_ci}; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_cistatic const struct wled_var_cfg wled3_string_i_limit_cfg = { 127362306a36Sopenharmony_ci .size = 26, 127462306a36Sopenharmony_ci}; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_cistatic const u32 wled4_string_i_limit_values[] = { 127762306a36Sopenharmony_ci 0, 2500, 5000, 7500, 10000, 12500, 15000, 17500, 20000, 127862306a36Sopenharmony_ci 22500, 25000, 27500, 30000, 127962306a36Sopenharmony_ci}; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_cistatic const struct wled_var_cfg wled4_string_i_limit_cfg = { 128262306a36Sopenharmony_ci .values = wled4_string_i_limit_values, 128362306a36Sopenharmony_ci .size = ARRAY_SIZE(wled4_string_i_limit_values), 128462306a36Sopenharmony_ci}; 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_cistatic const struct wled_var_cfg wled5_mod_sel_cfg = { 128762306a36Sopenharmony_ci .size = 2, 128862306a36Sopenharmony_ci}; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_cistatic const struct wled_var_cfg wled5_cabc_sel_cfg = { 129162306a36Sopenharmony_ci .size = 4, 129262306a36Sopenharmony_ci}; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_cistatic u32 wled_values(const struct wled_var_cfg *cfg, u32 idx) 129562306a36Sopenharmony_ci{ 129662306a36Sopenharmony_ci if (idx >= cfg->size) 129762306a36Sopenharmony_ci return UINT_MAX; 129862306a36Sopenharmony_ci if (cfg->fn) 129962306a36Sopenharmony_ci return cfg->fn(idx); 130062306a36Sopenharmony_ci if (cfg->values) 130162306a36Sopenharmony_ci return cfg->values[idx]; 130262306a36Sopenharmony_ci return idx; 130362306a36Sopenharmony_ci} 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_cistatic int wled_configure(struct wled *wled) 130662306a36Sopenharmony_ci{ 130762306a36Sopenharmony_ci struct wled_config *cfg = &wled->cfg; 130862306a36Sopenharmony_ci struct device *dev = wled->dev; 130962306a36Sopenharmony_ci const __be32 *prop_addr; 131062306a36Sopenharmony_ci u32 size, val, c; 131162306a36Sopenharmony_ci int rc, i, j, string_len; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci const struct wled_u32_opts *u32_opts = NULL; 131462306a36Sopenharmony_ci const struct wled_u32_opts wled3_opts[] = { 131562306a36Sopenharmony_ci { 131662306a36Sopenharmony_ci .name = "qcom,current-boost-limit", 131762306a36Sopenharmony_ci .val_ptr = &cfg->boost_i_limit, 131862306a36Sopenharmony_ci .cfg = &wled3_boost_i_limit_cfg, 131962306a36Sopenharmony_ci }, 132062306a36Sopenharmony_ci { 132162306a36Sopenharmony_ci .name = "qcom,current-limit", 132262306a36Sopenharmony_ci .val_ptr = &cfg->string_i_limit, 132362306a36Sopenharmony_ci .cfg = &wled3_string_i_limit_cfg, 132462306a36Sopenharmony_ci }, 132562306a36Sopenharmony_ci { 132662306a36Sopenharmony_ci .name = "qcom,ovp", 132762306a36Sopenharmony_ci .val_ptr = &cfg->ovp, 132862306a36Sopenharmony_ci .cfg = &wled3_ovp_cfg, 132962306a36Sopenharmony_ci }, 133062306a36Sopenharmony_ci { 133162306a36Sopenharmony_ci .name = "qcom,switching-freq", 133262306a36Sopenharmony_ci .val_ptr = &cfg->switch_freq, 133362306a36Sopenharmony_ci .cfg = &wled3_switch_freq_cfg, 133462306a36Sopenharmony_ci }, 133562306a36Sopenharmony_ci }; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci const struct wled_u32_opts wled4_opts[] = { 133862306a36Sopenharmony_ci { 133962306a36Sopenharmony_ci .name = "qcom,current-boost-limit", 134062306a36Sopenharmony_ci .val_ptr = &cfg->boost_i_limit, 134162306a36Sopenharmony_ci .cfg = &wled4_boost_i_limit_cfg, 134262306a36Sopenharmony_ci }, 134362306a36Sopenharmony_ci { 134462306a36Sopenharmony_ci .name = "qcom,current-limit-microamp", 134562306a36Sopenharmony_ci .val_ptr = &cfg->string_i_limit, 134662306a36Sopenharmony_ci .cfg = &wled4_string_i_limit_cfg, 134762306a36Sopenharmony_ci }, 134862306a36Sopenharmony_ci { 134962306a36Sopenharmony_ci .name = "qcom,ovp-millivolt", 135062306a36Sopenharmony_ci .val_ptr = &cfg->ovp, 135162306a36Sopenharmony_ci .cfg = &wled4_ovp_cfg, 135262306a36Sopenharmony_ci }, 135362306a36Sopenharmony_ci { 135462306a36Sopenharmony_ci .name = "qcom,switching-freq", 135562306a36Sopenharmony_ci .val_ptr = &cfg->switch_freq, 135662306a36Sopenharmony_ci .cfg = &wled3_switch_freq_cfg, 135762306a36Sopenharmony_ci }, 135862306a36Sopenharmony_ci }; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci const struct wled_u32_opts wled5_opts[] = { 136162306a36Sopenharmony_ci { 136262306a36Sopenharmony_ci .name = "qcom,current-boost-limit", 136362306a36Sopenharmony_ci .val_ptr = &cfg->boost_i_limit, 136462306a36Sopenharmony_ci .cfg = &wled5_boost_i_limit_cfg, 136562306a36Sopenharmony_ci }, 136662306a36Sopenharmony_ci { 136762306a36Sopenharmony_ci .name = "qcom,current-limit-microamp", 136862306a36Sopenharmony_ci .val_ptr = &cfg->string_i_limit, 136962306a36Sopenharmony_ci .cfg = &wled4_string_i_limit_cfg, 137062306a36Sopenharmony_ci }, 137162306a36Sopenharmony_ci { 137262306a36Sopenharmony_ci .name = "qcom,ovp-millivolt", 137362306a36Sopenharmony_ci .val_ptr = &cfg->ovp, 137462306a36Sopenharmony_ci .cfg = &wled5_ovp_cfg, 137562306a36Sopenharmony_ci }, 137662306a36Sopenharmony_ci { 137762306a36Sopenharmony_ci .name = "qcom,switching-freq", 137862306a36Sopenharmony_ci .val_ptr = &cfg->switch_freq, 137962306a36Sopenharmony_ci .cfg = &wled3_switch_freq_cfg, 138062306a36Sopenharmony_ci }, 138162306a36Sopenharmony_ci { 138262306a36Sopenharmony_ci .name = "qcom,modulator-sel", 138362306a36Sopenharmony_ci .val_ptr = &cfg->mod_sel, 138462306a36Sopenharmony_ci .cfg = &wled5_mod_sel_cfg, 138562306a36Sopenharmony_ci }, 138662306a36Sopenharmony_ci { 138762306a36Sopenharmony_ci .name = "qcom,cabc-sel", 138862306a36Sopenharmony_ci .val_ptr = &cfg->cabc_sel, 138962306a36Sopenharmony_ci .cfg = &wled5_cabc_sel_cfg, 139062306a36Sopenharmony_ci }, 139162306a36Sopenharmony_ci }; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci const struct wled_bool_opts bool_opts[] = { 139462306a36Sopenharmony_ci { "qcom,cs-out", &cfg->cs_out_en, }, 139562306a36Sopenharmony_ci { "qcom,ext-gen", &cfg->ext_gen, }, 139662306a36Sopenharmony_ci { "qcom,cabc", &cfg->cabc, }, 139762306a36Sopenharmony_ci { "qcom,external-pfet", &cfg->external_pfet, }, 139862306a36Sopenharmony_ci { "qcom,auto-string-detection", &cfg->auto_detection_enabled, }, 139962306a36Sopenharmony_ci }; 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci prop_addr = of_get_address(dev->of_node, 0, NULL, NULL); 140262306a36Sopenharmony_ci if (!prop_addr) { 140362306a36Sopenharmony_ci dev_err(wled->dev, "invalid IO resources\n"); 140462306a36Sopenharmony_ci return -EINVAL; 140562306a36Sopenharmony_ci } 140662306a36Sopenharmony_ci wled->ctrl_addr = be32_to_cpu(*prop_addr); 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci rc = of_property_read_string(dev->of_node, "label", &wled->name); 140962306a36Sopenharmony_ci if (rc) 141062306a36Sopenharmony_ci wled->name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node); 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci switch (wled->version) { 141362306a36Sopenharmony_ci case 3: 141462306a36Sopenharmony_ci u32_opts = wled3_opts; 141562306a36Sopenharmony_ci size = ARRAY_SIZE(wled3_opts); 141662306a36Sopenharmony_ci *cfg = wled3_config_defaults; 141762306a36Sopenharmony_ci wled->wled_set_brightness = wled3_set_brightness; 141862306a36Sopenharmony_ci wled->wled_sync_toggle = wled3_sync_toggle; 141962306a36Sopenharmony_ci wled->max_string_count = 3; 142062306a36Sopenharmony_ci wled->sink_addr = wled->ctrl_addr; 142162306a36Sopenharmony_ci break; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci case 4: 142462306a36Sopenharmony_ci u32_opts = wled4_opts; 142562306a36Sopenharmony_ci size = ARRAY_SIZE(wled4_opts); 142662306a36Sopenharmony_ci *cfg = wled4_config_defaults; 142762306a36Sopenharmony_ci wled->wled_set_brightness = wled4_set_brightness; 142862306a36Sopenharmony_ci wled->wled_sync_toggle = wled3_sync_toggle; 142962306a36Sopenharmony_ci wled->wled_cabc_config = wled4_cabc_config; 143062306a36Sopenharmony_ci wled->wled_ovp_delay = wled4_ovp_delay; 143162306a36Sopenharmony_ci wled->wled_auto_detection_required = 143262306a36Sopenharmony_ci wled4_auto_detection_required; 143362306a36Sopenharmony_ci wled->max_string_count = 4; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci prop_addr = of_get_address(dev->of_node, 1, NULL, NULL); 143662306a36Sopenharmony_ci if (!prop_addr) { 143762306a36Sopenharmony_ci dev_err(wled->dev, "invalid IO resources\n"); 143862306a36Sopenharmony_ci return -EINVAL; 143962306a36Sopenharmony_ci } 144062306a36Sopenharmony_ci wled->sink_addr = be32_to_cpu(*prop_addr); 144162306a36Sopenharmony_ci break; 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci case 5: 144462306a36Sopenharmony_ci u32_opts = wled5_opts; 144562306a36Sopenharmony_ci size = ARRAY_SIZE(wled5_opts); 144662306a36Sopenharmony_ci *cfg = wled5_config_defaults; 144762306a36Sopenharmony_ci wled->wled_set_brightness = wled5_set_brightness; 144862306a36Sopenharmony_ci wled->wled_sync_toggle = wled3_sync_toggle; 144962306a36Sopenharmony_ci wled->wled_cabc_config = wled5_cabc_config; 145062306a36Sopenharmony_ci wled->wled_ovp_delay = wled5_ovp_delay; 145162306a36Sopenharmony_ci wled->wled_auto_detection_required = 145262306a36Sopenharmony_ci wled5_auto_detection_required; 145362306a36Sopenharmony_ci wled->max_string_count = 4; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci prop_addr = of_get_address(dev->of_node, 1, NULL, NULL); 145662306a36Sopenharmony_ci if (!prop_addr) { 145762306a36Sopenharmony_ci dev_err(wled->dev, "invalid IO resources\n"); 145862306a36Sopenharmony_ci return -EINVAL; 145962306a36Sopenharmony_ci } 146062306a36Sopenharmony_ci wled->sink_addr = be32_to_cpu(*prop_addr); 146162306a36Sopenharmony_ci break; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci default: 146462306a36Sopenharmony_ci dev_err(wled->dev, "Invalid WLED version\n"); 146562306a36Sopenharmony_ci return -EINVAL; 146662306a36Sopenharmony_ci } 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci for (i = 0; i < size; ++i) { 146962306a36Sopenharmony_ci rc = of_property_read_u32(dev->of_node, u32_opts[i].name, &val); 147062306a36Sopenharmony_ci if (rc == -EINVAL) { 147162306a36Sopenharmony_ci continue; 147262306a36Sopenharmony_ci } else if (rc) { 147362306a36Sopenharmony_ci dev_err(dev, "error reading '%s'\n", u32_opts[i].name); 147462306a36Sopenharmony_ci return rc; 147562306a36Sopenharmony_ci } 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci c = UINT_MAX; 147862306a36Sopenharmony_ci for (j = 0; c != val; j++) { 147962306a36Sopenharmony_ci c = wled_values(u32_opts[i].cfg, j); 148062306a36Sopenharmony_ci if (c == UINT_MAX) { 148162306a36Sopenharmony_ci dev_err(dev, "invalid value for '%s'\n", 148262306a36Sopenharmony_ci u32_opts[i].name); 148362306a36Sopenharmony_ci return -EINVAL; 148462306a36Sopenharmony_ci } 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci if (c == val) 148762306a36Sopenharmony_ci break; 148862306a36Sopenharmony_ci } 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci dev_dbg(dev, "'%s' = %u\n", u32_opts[i].name, c); 149162306a36Sopenharmony_ci *u32_opts[i].val_ptr = j; 149262306a36Sopenharmony_ci } 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(bool_opts); ++i) { 149562306a36Sopenharmony_ci if (of_property_read_bool(dev->of_node, bool_opts[i].name)) 149662306a36Sopenharmony_ci *bool_opts[i].val_ptr = true; 149762306a36Sopenharmony_ci } 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci string_len = of_property_count_elems_of_size(dev->of_node, 150062306a36Sopenharmony_ci "qcom,enabled-strings", 150162306a36Sopenharmony_ci sizeof(u32)); 150262306a36Sopenharmony_ci if (string_len > 0) { 150362306a36Sopenharmony_ci if (string_len > wled->max_string_count) { 150462306a36Sopenharmony_ci dev_err(dev, "Cannot have more than %d strings\n", 150562306a36Sopenharmony_ci wled->max_string_count); 150662306a36Sopenharmony_ci return -EINVAL; 150762306a36Sopenharmony_ci } 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci rc = of_property_read_u32_array(dev->of_node, 151062306a36Sopenharmony_ci "qcom,enabled-strings", 151162306a36Sopenharmony_ci wled->cfg.enabled_strings, 151262306a36Sopenharmony_ci string_len); 151362306a36Sopenharmony_ci if (rc) { 151462306a36Sopenharmony_ci dev_err(dev, "Failed to read %d elements from qcom,enabled-strings: %d\n", 151562306a36Sopenharmony_ci string_len, rc); 151662306a36Sopenharmony_ci return rc; 151762306a36Sopenharmony_ci } 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci for (i = 0; i < string_len; ++i) { 152062306a36Sopenharmony_ci if (wled->cfg.enabled_strings[i] >= wled->max_string_count) { 152162306a36Sopenharmony_ci dev_err(dev, 152262306a36Sopenharmony_ci "qcom,enabled-strings index %d at %d is out of bounds\n", 152362306a36Sopenharmony_ci wled->cfg.enabled_strings[i], i); 152462306a36Sopenharmony_ci return -EINVAL; 152562306a36Sopenharmony_ci } 152662306a36Sopenharmony_ci } 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci cfg->num_strings = string_len; 152962306a36Sopenharmony_ci } 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci rc = of_property_read_u32(dev->of_node, "qcom,num-strings", &val); 153262306a36Sopenharmony_ci if (!rc) { 153362306a36Sopenharmony_ci if (val < 1 || val > wled->max_string_count) { 153462306a36Sopenharmony_ci dev_err(dev, "qcom,num-strings must be between 1 and %d\n", 153562306a36Sopenharmony_ci wled->max_string_count); 153662306a36Sopenharmony_ci return -EINVAL; 153762306a36Sopenharmony_ci } 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci if (string_len > 0) { 154062306a36Sopenharmony_ci dev_warn(dev, "Only one of qcom,num-strings or qcom,enabled-strings" 154162306a36Sopenharmony_ci " should be set\n"); 154262306a36Sopenharmony_ci if (val > string_len) { 154362306a36Sopenharmony_ci dev_err(dev, "qcom,num-strings exceeds qcom,enabled-strings\n"); 154462306a36Sopenharmony_ci return -EINVAL; 154562306a36Sopenharmony_ci } 154662306a36Sopenharmony_ci } 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci cfg->num_strings = val; 154962306a36Sopenharmony_ci } 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci return 0; 155262306a36Sopenharmony_ci} 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_cistatic int wled_configure_short_irq(struct wled *wled, 155562306a36Sopenharmony_ci struct platform_device *pdev) 155662306a36Sopenharmony_ci{ 155762306a36Sopenharmony_ci int rc; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci if (!wled->has_short_detect) 156062306a36Sopenharmony_ci return 0; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + 156362306a36Sopenharmony_ci WLED4_CTRL_REG_SHORT_PROTECT, 156462306a36Sopenharmony_ci WLED4_CTRL_REG_SHORT_EN_MASK, 156562306a36Sopenharmony_ci WLED4_CTRL_REG_SHORT_EN_MASK); 156662306a36Sopenharmony_ci if (rc < 0) 156762306a36Sopenharmony_ci return rc; 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci wled->short_irq = platform_get_irq_byname(pdev, "short"); 157062306a36Sopenharmony_ci if (wled->short_irq < 0) { 157162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "short irq is not used\n"); 157262306a36Sopenharmony_ci return 0; 157362306a36Sopenharmony_ci } 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci rc = devm_request_threaded_irq(wled->dev, wled->short_irq, 157662306a36Sopenharmony_ci NULL, wled_short_irq_handler, 157762306a36Sopenharmony_ci IRQF_ONESHOT, 157862306a36Sopenharmony_ci "wled_short_irq", wled); 157962306a36Sopenharmony_ci if (rc < 0) 158062306a36Sopenharmony_ci dev_err(wled->dev, "Unable to request short_irq (err:%d)\n", 158162306a36Sopenharmony_ci rc); 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci return rc; 158462306a36Sopenharmony_ci} 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_cistatic int wled_configure_ovp_irq(struct wled *wled, 158762306a36Sopenharmony_ci struct platform_device *pdev) 158862306a36Sopenharmony_ci{ 158962306a36Sopenharmony_ci int rc; 159062306a36Sopenharmony_ci u32 val; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci wled->ovp_irq = platform_get_irq_byname(pdev, "ovp"); 159362306a36Sopenharmony_ci if (wled->ovp_irq < 0) { 159462306a36Sopenharmony_ci dev_dbg(&pdev->dev, "OVP IRQ not found - disabling automatic string detection\n"); 159562306a36Sopenharmony_ci return 0; 159662306a36Sopenharmony_ci } 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci rc = devm_request_threaded_irq(wled->dev, wled->ovp_irq, NULL, 159962306a36Sopenharmony_ci wled_ovp_irq_handler, IRQF_ONESHOT, 160062306a36Sopenharmony_ci "wled_ovp_irq", wled); 160162306a36Sopenharmony_ci if (rc < 0) { 160262306a36Sopenharmony_ci dev_err(wled->dev, "Unable to request ovp_irq (err:%d)\n", 160362306a36Sopenharmony_ci rc); 160462306a36Sopenharmony_ci wled->ovp_irq = 0; 160562306a36Sopenharmony_ci return 0; 160662306a36Sopenharmony_ci } 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci rc = regmap_read(wled->regmap, wled->ctrl_addr + 160962306a36Sopenharmony_ci WLED3_CTRL_REG_MOD_EN, &val); 161062306a36Sopenharmony_ci if (rc < 0) 161162306a36Sopenharmony_ci return rc; 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci /* Keep OVP irq disabled until module is enabled */ 161462306a36Sopenharmony_ci if (!(val & WLED3_CTRL_REG_MOD_EN_MASK)) 161562306a36Sopenharmony_ci disable_irq(wled->ovp_irq); 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci return 0; 161862306a36Sopenharmony_ci} 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_cistatic const struct backlight_ops wled_ops = { 162162306a36Sopenharmony_ci .update_status = wled_update_status, 162262306a36Sopenharmony_ci}; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_cistatic int wled_probe(struct platform_device *pdev) 162562306a36Sopenharmony_ci{ 162662306a36Sopenharmony_ci struct backlight_properties props; 162762306a36Sopenharmony_ci struct backlight_device *bl; 162862306a36Sopenharmony_ci struct wled *wled; 162962306a36Sopenharmony_ci struct regmap *regmap; 163062306a36Sopenharmony_ci u32 val; 163162306a36Sopenharmony_ci int rc; 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci regmap = dev_get_regmap(pdev->dev.parent, NULL); 163462306a36Sopenharmony_ci if (!regmap) { 163562306a36Sopenharmony_ci dev_err(&pdev->dev, "Unable to get regmap\n"); 163662306a36Sopenharmony_ci return -EINVAL; 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci wled = devm_kzalloc(&pdev->dev, sizeof(*wled), GFP_KERNEL); 164062306a36Sopenharmony_ci if (!wled) 164162306a36Sopenharmony_ci return -ENOMEM; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci wled->regmap = regmap; 164462306a36Sopenharmony_ci wled->dev = &pdev->dev; 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci wled->version = (uintptr_t)of_device_get_match_data(&pdev->dev); 164762306a36Sopenharmony_ci if (!wled->version) { 164862306a36Sopenharmony_ci dev_err(&pdev->dev, "Unknown device version\n"); 164962306a36Sopenharmony_ci return -ENODEV; 165062306a36Sopenharmony_ci } 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci mutex_init(&wled->lock); 165362306a36Sopenharmony_ci rc = wled_configure(wled); 165462306a36Sopenharmony_ci if (rc) 165562306a36Sopenharmony_ci return rc; 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci val = WLED3_SINK_REG_BRIGHT_MAX; 165862306a36Sopenharmony_ci of_property_read_u32(pdev->dev.of_node, "max-brightness", &val); 165962306a36Sopenharmony_ci wled->max_brightness = val; 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci switch (wled->version) { 166262306a36Sopenharmony_ci case 3: 166362306a36Sopenharmony_ci wled->cfg.auto_detection_enabled = false; 166462306a36Sopenharmony_ci rc = wled3_setup(wled); 166562306a36Sopenharmony_ci if (rc) { 166662306a36Sopenharmony_ci dev_err(&pdev->dev, "wled3_setup failed\n"); 166762306a36Sopenharmony_ci return rc; 166862306a36Sopenharmony_ci } 166962306a36Sopenharmony_ci break; 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci case 4: 167262306a36Sopenharmony_ci wled->has_short_detect = true; 167362306a36Sopenharmony_ci rc = wled4_setup(wled); 167462306a36Sopenharmony_ci if (rc) { 167562306a36Sopenharmony_ci dev_err(&pdev->dev, "wled4_setup failed\n"); 167662306a36Sopenharmony_ci return rc; 167762306a36Sopenharmony_ci } 167862306a36Sopenharmony_ci break; 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci case 5: 168162306a36Sopenharmony_ci wled->has_short_detect = true; 168262306a36Sopenharmony_ci if (wled->cfg.cabc_sel) 168362306a36Sopenharmony_ci wled->max_brightness = WLED5_SINK_REG_BRIGHT_MAX_12B; 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci rc = wled5_setup(wled); 168662306a36Sopenharmony_ci if (rc) { 168762306a36Sopenharmony_ci dev_err(&pdev->dev, "wled5_setup failed\n"); 168862306a36Sopenharmony_ci return rc; 168962306a36Sopenharmony_ci } 169062306a36Sopenharmony_ci break; 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci default: 169362306a36Sopenharmony_ci dev_err(wled->dev, "Invalid WLED version\n"); 169462306a36Sopenharmony_ci break; 169562306a36Sopenharmony_ci } 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci INIT_DELAYED_WORK(&wled->ovp_work, wled_ovp_work); 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci rc = wled_configure_short_irq(wled, pdev); 170062306a36Sopenharmony_ci if (rc < 0) 170162306a36Sopenharmony_ci return rc; 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci rc = wled_configure_ovp_irq(wled, pdev); 170462306a36Sopenharmony_ci if (rc < 0) 170562306a36Sopenharmony_ci return rc; 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci val = WLED_DEFAULT_BRIGHTNESS; 170862306a36Sopenharmony_ci of_property_read_u32(pdev->dev.of_node, "default-brightness", &val); 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci memset(&props, 0, sizeof(struct backlight_properties)); 171162306a36Sopenharmony_ci props.type = BACKLIGHT_RAW; 171262306a36Sopenharmony_ci props.brightness = val; 171362306a36Sopenharmony_ci props.max_brightness = wled->max_brightness; 171462306a36Sopenharmony_ci bl = devm_backlight_device_register(&pdev->dev, wled->name, 171562306a36Sopenharmony_ci &pdev->dev, wled, 171662306a36Sopenharmony_ci &wled_ops, &props); 171762306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(bl); 171862306a36Sopenharmony_ci}; 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_cistatic void wled_remove(struct platform_device *pdev) 172162306a36Sopenharmony_ci{ 172262306a36Sopenharmony_ci struct wled *wled = platform_get_drvdata(pdev); 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci mutex_destroy(&wled->lock); 172562306a36Sopenharmony_ci cancel_delayed_work_sync(&wled->ovp_work); 172662306a36Sopenharmony_ci disable_irq(wled->short_irq); 172762306a36Sopenharmony_ci disable_irq(wled->ovp_irq); 172862306a36Sopenharmony_ci} 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_cistatic const struct of_device_id wled_match_table[] = { 173162306a36Sopenharmony_ci { .compatible = "qcom,pm8941-wled", .data = (void *)3 }, 173262306a36Sopenharmony_ci { .compatible = "qcom,pmi8950-wled", .data = (void *)4 }, 173362306a36Sopenharmony_ci { .compatible = "qcom,pmi8994-wled", .data = (void *)4 }, 173462306a36Sopenharmony_ci { .compatible = "qcom,pmi8998-wled", .data = (void *)4 }, 173562306a36Sopenharmony_ci { .compatible = "qcom,pm660l-wled", .data = (void *)4 }, 173662306a36Sopenharmony_ci { .compatible = "qcom,pm6150l-wled", .data = (void *)5 }, 173762306a36Sopenharmony_ci { .compatible = "qcom,pm8150l-wled", .data = (void *)5 }, 173862306a36Sopenharmony_ci {} 173962306a36Sopenharmony_ci}; 174062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, wled_match_table); 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_cistatic struct platform_driver wled_driver = { 174362306a36Sopenharmony_ci .probe = wled_probe, 174462306a36Sopenharmony_ci .remove_new = wled_remove, 174562306a36Sopenharmony_ci .driver = { 174662306a36Sopenharmony_ci .name = "qcom,wled", 174762306a36Sopenharmony_ci .of_match_table = wled_match_table, 174862306a36Sopenharmony_ci }, 174962306a36Sopenharmony_ci}; 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_cimodule_platform_driver(wled_driver); 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm WLED driver"); 175462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1755