2019-05-29 17:17:58 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2015-03-12 18:47:08 +03:00
/* Copyright (c) 2015, Sony Mobile Communications, AB.
*/
2019-11-01 09:27:03 +03:00
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/ktime.h>
2015-03-12 18:47:08 +03:00
# include <linux/kernel.h>
2015-07-22 03:44:49 +03:00
# include <linux/backlight.h>
2015-03-12 18:47:08 +03:00
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_device.h>
2019-11-01 09:27:01 +03:00
# include <linux/of_address.h>
2015-03-12 18:47:08 +03:00
# include <linux/regmap.h>
2015-10-26 20:45:08 +03:00
/* From DT binding */
2019-11-01 09:27:01 +03:00
# define WLED_MAX_STRINGS 4
2020-04-23 18:33:37 +03:00
# define MOD_A 0
# define MOD_B 1
2019-11-01 09:27:01 +03:00
2019-11-01 09:27:00 +03:00
# define WLED_DEFAULT_BRIGHTNESS 2048
2019-11-01 09:27:04 +03:00
# define WLED_SOFT_START_DLY_US 10000
2019-11-01 09:27:00 +03:00
# define WLED3_SINK_REG_BRIGHT_MAX 0xFFF
2020-04-23 18:33:37 +03:00
# define WLED5_SINK_REG_BRIGHT_MAX_12B 0xFFF
# define WLED5_SINK_REG_BRIGHT_MAX_15B 0x7FFF
2015-03-12 18:47:08 +03:00
2019-11-01 09:27:02 +03:00
/* WLED3/WLED4 control registers */
2019-11-01 09:27:04 +03:00
# define WLED3_CTRL_REG_FAULT_STATUS 0x08
# define WLED3_CTRL_REG_ILIM_FAULT_BIT BIT(0)
# define WLED3_CTRL_REG_OVP_FAULT_BIT BIT(1)
# define WLED4_CTRL_REG_SC_FAULT_BIT BIT(2)
2020-04-23 18:33:37 +03:00
# define WLED5_CTRL_REG_OVP_PRE_ALARM_BIT BIT(4)
2019-11-01 09:27:04 +03:00
# define WLED3_CTRL_REG_INT_RT_STS 0x10
# define WLED3_CTRL_REG_OVP_FAULT_STATUS BIT(1)
2019-11-01 09:27:00 +03:00
# define WLED3_CTRL_REG_MOD_EN 0x46
# define WLED3_CTRL_REG_MOD_EN_MASK BIT(7)
2019-11-01 09:27:01 +03:00
# define WLED3_CTRL_REG_MOD_EN_SHIFT 7
2015-03-12 18:47:08 +03:00
2019-11-01 09:27:04 +03:00
# define WLED3_CTRL_REG_FEEDBACK_CONTROL 0x48
2019-11-01 09:27:00 +03:00
# define WLED3_CTRL_REG_FREQ 0x4c
2019-11-01 09:27:01 +03:00
# define WLED3_CTRL_REG_FREQ_MASK GENMASK(3, 0)
2015-03-12 18:47:08 +03:00
2019-11-01 09:27:00 +03:00
# define WLED3_CTRL_REG_OVP 0x4d
2019-11-01 09:27:04 +03:00
# define WLED3_CTRL_REG_OVP_MASK GENMASK(1, 0)
2020-04-23 18:33:37 +03:00
# define WLED5_CTRL_REG_OVP_MASK GENMASK(3, 0)
2015-03-12 18:47:08 +03:00
2019-11-01 09:27:00 +03:00
# define WLED3_CTRL_REG_ILIMIT 0x4e
2019-11-01 09:27:01 +03:00
# define WLED3_CTRL_REG_ILIMIT_MASK GENMASK(2, 0)
2015-03-12 18:47:08 +03:00
2019-11-01 09:27:02 +03:00
/* WLED3/WLED4 sink registers */
2019-11-01 09:27:00 +03:00
# define WLED3_SINK_REG_SYNC 0x47
# define WLED3_SINK_REG_SYNC_CLEAR 0x00
2015-03-12 18:47:08 +03:00
2019-11-01 09:27:00 +03:00
# define WLED3_SINK_REG_CURR_SINK 0x4f
2019-11-01 09:27:01 +03:00
# define WLED3_SINK_REG_CURR_SINK_MASK GENMASK(7, 5)
# define WLED3_SINK_REG_CURR_SINK_SHFT 5
2015-03-12 18:47:08 +03:00
2019-11-01 09:27:01 +03:00
/* WLED3 specific per-'string' registers below */
# define WLED3_SINK_REG_BRIGHT(n) (0x40 + n)
2015-03-12 18:47:08 +03:00
2019-11-01 09:27:01 +03:00
# define WLED3_SINK_REG_STR_MOD_EN(n) (0x60 + (n * 0x10))
2019-11-01 09:27:00 +03:00
# define WLED3_SINK_REG_STR_MOD_MASK BIT(7)
2015-03-12 18:47:08 +03:00
2019-11-01 09:27:01 +03:00
# define WLED3_SINK_REG_STR_FULL_SCALE_CURR(n) (0x62 + (n * 0x10))
# define WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK GENMASK(4, 0)
2015-03-12 18:47:08 +03:00
2019-11-01 09:27:01 +03:00
# define WLED3_SINK_REG_STR_MOD_SRC(n) (0x63 + (n * 0x10))
# define WLED3_SINK_REG_STR_MOD_SRC_MASK BIT(0)
2019-11-01 09:27:00 +03:00
# define WLED3_SINK_REG_STR_MOD_SRC_INT 0x00
# define WLED3_SINK_REG_STR_MOD_SRC_EXT 0x01
2015-03-12 18:47:08 +03:00
2019-11-01 09:27:01 +03:00
# define WLED3_SINK_REG_STR_CABC(n) (0x66 + (n * 0x10))
2019-11-01 09:27:00 +03:00
# define WLED3_SINK_REG_STR_CABC_MASK BIT(7)
2019-11-01 09:27:01 +03:00
2019-11-01 09:27:03 +03:00
/* WLED4 specific control registers */
# define WLED4_CTRL_REG_SHORT_PROTECT 0x5e
# define WLED4_CTRL_REG_SHORT_EN_MASK BIT(7)
# define WLED4_CTRL_REG_SEC_ACCESS 0xd0
# define WLED4_CTRL_REG_SEC_UNLOCK 0xa5
# define WLED4_CTRL_REG_TEST1 0xe2
# define WLED4_CTRL_REG_TEST1_EXT_FET_DTEST2 0x09
2019-11-01 09:27:02 +03:00
/* WLED4 specific sink registers */
# define WLED4_SINK_REG_CURR_SINK 0x46
# define WLED4_SINK_REG_CURR_SINK_MASK GENMASK(7, 4)
# define WLED4_SINK_REG_CURR_SINK_SHFT 4
/* WLED4 specific per-'string' registers below */
# define WLED4_SINK_REG_STR_MOD_EN(n) (0x50 + (n * 0x10))
# define WLED4_SINK_REG_STR_MOD_MASK BIT(7)
# define WLED4_SINK_REG_STR_FULL_SCALE_CURR(n) (0x52 + (n * 0x10))
# define WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK GENMASK(3, 0)
# define WLED4_SINK_REG_STR_MOD_SRC(n) (0x53 + (n * 0x10))
# define WLED4_SINK_REG_STR_MOD_SRC_MASK BIT(0)
# define WLED4_SINK_REG_STR_MOD_SRC_INT 0x00
# define WLED4_SINK_REG_STR_MOD_SRC_EXT 0x01
# define WLED4_SINK_REG_STR_CABC(n) (0x56 + (n * 0x10))
# define WLED4_SINK_REG_STR_CABC_MASK BIT(7)
# define WLED4_SINK_REG_BRIGHT(n) (0x57 + (n * 0x10))
2020-04-23 18:33:37 +03:00
/* WLED5 specific control registers */
# define WLED5_CTRL_REG_OVP_INT_CTL 0x5f
# define WLED5_CTRL_REG_OVP_INT_TIMER_MASK GENMASK(2, 0)
/* WLED5 specific sink registers */
# define WLED5_SINK_REG_MOD_A_EN 0x50
# define WLED5_SINK_REG_MOD_B_EN 0x60
# define WLED5_SINK_REG_MOD_EN_MASK BIT(7)
# define WLED5_SINK_REG_MOD_A_SRC_SEL 0x51
# define WLED5_SINK_REG_MOD_B_SRC_SEL 0x61
# define WLED5_SINK_REG_MOD_SRC_SEL_HIGH 0
# define WLED5_SINK_REG_MOD_SRC_SEL_EXT 0x03
# define WLED5_SINK_REG_MOD_SRC_SEL_MASK GENMASK(1, 0)
# define WLED5_SINK_REG_MOD_A_BRIGHTNESS_WIDTH_SEL 0x52
# define WLED5_SINK_REG_MOD_B_BRIGHTNESS_WIDTH_SEL 0x62
# define WLED5_SINK_REG_BRIGHTNESS_WIDTH_12B 0
# define WLED5_SINK_REG_BRIGHTNESS_WIDTH_15B 1
# define WLED5_SINK_REG_MOD_A_BRIGHTNESS_LSB 0x53
# define WLED5_SINK_REG_MOD_A_BRIGHTNESS_MSB 0x54
# define WLED5_SINK_REG_MOD_B_BRIGHTNESS_LSB 0x63
# define WLED5_SINK_REG_MOD_B_BRIGHTNESS_MSB 0x64
# define WLED5_SINK_REG_MOD_SYNC_BIT 0x65
# define WLED5_SINK_REG_SYNC_MOD_A_BIT BIT(0)
# define WLED5_SINK_REG_SYNC_MOD_B_BIT BIT(1)
# define WLED5_SINK_REG_SYNC_MASK GENMASK(1, 0)
/* WLED5 specific per-'string' registers below */
# define WLED5_SINK_REG_STR_FULL_SCALE_CURR(n) (0x72 + (n * 0x10))
# define WLED5_SINK_REG_STR_SRC_SEL(n) (0x73 + (n * 0x10))
# define WLED5_SINK_REG_SRC_SEL_MOD_A 0
# define WLED5_SINK_REG_SRC_SEL_MOD_B 1
# define WLED5_SINK_REG_SRC_SEL_MASK GENMASK(1, 0)
2019-11-01 09:27:01 +03:00
struct wled_var_cfg {
const u32 * values ;
u32 ( * fn ) ( u32 ) ;
int size ;
} ;
struct wled_u32_opts {
const char * name ;
u32 * val_ptr ;
const struct wled_var_cfg * cfg ;
} ;
struct wled_bool_opts {
const char * name ;
bool * val_ptr ;
} ;
2015-03-12 18:47:08 +03:00
2019-11-01 09:27:00 +03:00
struct wled_config {
u32 boost_i_limit ;
2015-03-12 18:47:08 +03:00
u32 ovp ;
u32 switch_freq ;
u32 num_strings ;
2019-11-01 09:27:00 +03:00
u32 string_i_limit ;
2019-11-01 09:27:01 +03:00
u32 enabled_strings [ WLED_MAX_STRINGS ] ;
2020-04-23 18:33:37 +03:00
u32 mod_sel ;
u32 cabc_sel ;
2015-03-12 18:47:08 +03:00
bool cs_out_en ;
bool ext_gen ;
2019-11-01 09:27:01 +03:00
bool cabc ;
2019-11-01 09:27:03 +03:00
bool external_pfet ;
2019-11-01 09:27:04 +03:00
bool auto_detection_enabled ;
2015-03-12 18:47:08 +03:00
} ;
2019-11-01 09:27:00 +03:00
struct wled {
2015-07-22 03:44:49 +03:00
const char * name ;
2019-11-01 09:27:01 +03:00
struct device * dev ;
2015-03-12 18:47:08 +03:00
struct regmap * regmap ;
2019-11-01 09:27:03 +03:00
struct mutex lock ; /* Lock to avoid race from thread irq handler */
ktime_t last_short_event ;
2019-11-01 09:27:04 +03:00
ktime_t start_ovp_fault_time ;
2019-11-01 09:27:01 +03:00
u16 ctrl_addr ;
2019-11-01 09:27:02 +03:00
u16 sink_addr ;
2019-11-01 09:27:01 +03:00
u16 max_string_count ;
2019-11-01 09:27:04 +03:00
u16 auto_detection_ovp_count ;
2019-11-01 09:27:01 +03:00
u32 brightness ;
u32 max_brightness ;
2019-11-01 09:27:03 +03:00
u32 short_count ;
2019-11-01 09:27:04 +03:00
u32 auto_detect_count ;
2020-04-23 18:33:35 +03:00
u32 version ;
2019-11-01 09:27:03 +03:00
bool disabled_by_short ;
bool has_short_detect ;
2020-04-23 18:33:37 +03:00
bool cabc_disabled ;
2019-11-01 09:27:03 +03:00
int short_irq ;
2019-11-01 09:27:04 +03:00
int ovp_irq ;
2015-03-12 18:47:08 +03:00
2019-11-01 09:27:00 +03:00
struct wled_config cfg ;
2019-11-01 09:27:04 +03:00
struct delayed_work ovp_work ;
2020-04-23 18:33:35 +03:00
/* Configures the brightness. Applicable for wled3, wled4 and wled5 */
2019-11-01 09:27:01 +03:00
int ( * wled_set_brightness ) ( struct wled * wled , u16 brightness ) ;
2020-04-23 18:33:35 +03:00
/* Configures the cabc register. Applicable for wled4 and wled5 */
int ( * wled_cabc_config ) ( struct wled * wled , bool enable ) ;
/*
* Toggles the sync bit for the brightness update to take place .
* Applicable for WLED3 , WLED4 and WLED5 .
*/
int ( * wled_sync_toggle ) ( struct wled * wled ) ;
/*
* Time to wait before checking the OVP status after wled module enable .
* Applicable for WLED4 and WLED5 .
*/
int ( * wled_ovp_delay ) ( struct wled * wled ) ;
/*
* Determines if the auto string detection is required .
* Applicable for WLED4 and WLED5
*/
bool ( * wled_auto_detection_required ) ( struct wled * wled ) ;
2015-03-12 18:47:08 +03:00
} ;
2019-11-01 09:27:01 +03:00
static int wled3_set_brightness ( struct wled * wled , u16 brightness )
{
int rc , i ;
2021-11-15 23:34:53 +03:00
__le16 v ;
2019-11-01 09:27:01 +03:00
2021-11-15 23:34:53 +03:00
v = cpu_to_le16 ( brightness & WLED3_SINK_REG_BRIGHT_MAX ) ;
2019-11-01 09:27:01 +03:00
2021-11-15 23:34:58 +03:00
for ( i = 0 ; i < wled - > cfg . num_strings ; + + i ) {
2019-11-01 09:27:01 +03:00
rc = regmap_bulk_write ( wled - > regmap , wled - > ctrl_addr +
2021-11-15 23:34:59 +03:00
WLED3_SINK_REG_BRIGHT ( wled - > cfg . enabled_strings [ i ] ) ,
2021-11-15 23:34:53 +03:00
& v , sizeof ( v ) ) ;
2019-11-01 09:27:01 +03:00
if ( rc < 0 )
return rc ;
}
return 0 ;
}
2019-11-01 09:27:02 +03:00
static int wled4_set_brightness ( struct wled * wled , u16 brightness )
{
int rc , i ;
u16 low_limit = wled - > max_brightness * 4 / 1000 ;
2021-11-15 23:34:53 +03:00
__le16 v ;
2019-11-01 09:27:02 +03:00
/* WLED4's lower limit of operation is 0.4% */
if ( brightness > 0 & & brightness < low_limit )
brightness = low_limit ;
2021-11-15 23:34:53 +03:00
v = cpu_to_le16 ( brightness & WLED3_SINK_REG_BRIGHT_MAX ) ;
2019-11-01 09:27:02 +03:00
2021-11-15 23:34:58 +03:00
for ( i = 0 ; i < wled - > cfg . num_strings ; + + i ) {
2019-11-01 09:27:02 +03:00
rc = regmap_bulk_write ( wled - > regmap , wled - > sink_addr +
2021-11-15 23:34:59 +03:00
WLED4_SINK_REG_BRIGHT ( wled - > cfg . enabled_strings [ i ] ) ,
2021-11-15 23:34:53 +03:00
& v , sizeof ( v ) ) ;
2019-11-01 09:27:02 +03:00
if ( rc < 0 )
return rc ;
}
return 0 ;
}
2020-04-23 18:33:37 +03:00
static int wled5_set_brightness ( struct wled * wled , u16 brightness )
{
int rc , offset ;
u16 low_limit = wled - > max_brightness * 1 / 1000 ;
2021-11-15 23:34:53 +03:00
__le16 v ;
2020-04-23 18:33:37 +03:00
/* WLED5's lower limit is 0.1% */
if ( brightness < low_limit )
brightness = low_limit ;
2021-11-15 23:34:53 +03:00
v = cpu_to_le16 ( brightness & WLED5_SINK_REG_BRIGHT_MAX_15B ) ;
2020-04-23 18:33:37 +03:00
offset = ( wled - > cfg . mod_sel = = MOD_A ) ?
WLED5_SINK_REG_MOD_A_BRIGHTNESS_LSB :
WLED5_SINK_REG_MOD_B_BRIGHTNESS_LSB ;
rc = regmap_bulk_write ( wled - > regmap , wled - > sink_addr + offset ,
2021-11-15 23:34:53 +03:00
& v , sizeof ( v ) ) ;
2020-04-23 18:33:37 +03:00
return rc ;
}
2019-11-01 09:27:04 +03:00
static void wled_ovp_work ( struct work_struct * work )
{
struct wled * wled = container_of ( work ,
struct wled , ovp_work . work ) ;
enable_irq ( wled - > ovp_irq ) ;
}
2019-11-01 09:27:01 +03:00
static int wled_module_enable ( struct wled * wled , int val )
{
int rc ;
2019-11-01 09:27:03 +03:00
if ( wled - > disabled_by_short )
return - ENXIO ;
2019-11-01 09:27:01 +03:00
rc = regmap_update_bits ( wled - > regmap , wled - > ctrl_addr +
WLED3_CTRL_REG_MOD_EN ,
WLED3_CTRL_REG_MOD_EN_MASK ,
val < < WLED3_CTRL_REG_MOD_EN_SHIFT ) ;
2019-11-01 09:27:04 +03:00
if ( rc < 0 )
return rc ;
if ( wled - > ovp_irq > 0 ) {
if ( val ) {
/*
* The hardware generates a storm of spurious OVP
* interrupts during soft start operations . So defer
* enabling the IRQ for 10 ms to ensure that the
* soft start is complete .
*/
schedule_delayed_work ( & wled - > ovp_work , HZ / 100 ) ;
} else {
if ( ! cancel_delayed_work_sync ( & wled - > ovp_work ) )
disable_irq ( wled - > ovp_irq ) ;
}
}
return 0 ;
2019-11-01 09:27:01 +03:00
}
2020-04-23 18:33:35 +03:00
static int wled3_sync_toggle ( struct wled * wled )
2019-11-01 09:27:01 +03:00
{
int rc ;
unsigned int mask = GENMASK ( wled - > max_string_count - 1 , 0 ) ;
rc = regmap_update_bits ( wled - > regmap ,
2021-03-14 13:11:10 +03:00
wled - > sink_addr + WLED3_SINK_REG_SYNC ,
2021-03-18 15:39:40 +03:00
mask , WLED3_SINK_REG_SYNC_CLEAR ) ;
2019-11-01 09:27:01 +03:00
if ( rc < 0 )
return rc ;
rc = regmap_update_bits ( wled - > regmap ,
2021-03-14 13:11:10 +03:00
wled - > sink_addr + WLED3_SINK_REG_SYNC ,
2021-03-18 15:39:40 +03:00
mask , mask ) ;
2019-11-01 09:27:01 +03:00
return rc ;
}
2021-03-18 15:39:39 +03:00
static int wled5_mod_sync_toggle ( struct wled * wled )
2020-04-23 18:33:37 +03:00
{
int rc ;
u8 val ;
rc = regmap_update_bits ( wled - > regmap ,
wled - > sink_addr + WLED5_SINK_REG_MOD_SYNC_BIT ,
2021-03-18 15:39:40 +03:00
WLED5_SINK_REG_SYNC_MASK , 0 ) ;
2020-04-23 18:33:37 +03:00
if ( rc < 0 )
return rc ;
2021-03-18 15:39:40 +03:00
val = ( wled - > cfg . mod_sel = = MOD_A ) ? WLED5_SINK_REG_SYNC_MOD_A_BIT :
WLED5_SINK_REG_SYNC_MOD_B_BIT ;
2020-04-23 18:33:37 +03:00
return regmap_update_bits ( wled - > regmap ,
wled - > sink_addr + WLED5_SINK_REG_MOD_SYNC_BIT ,
2021-03-18 15:39:40 +03:00
WLED5_SINK_REG_SYNC_MASK , val ) ;
2020-04-23 18:33:37 +03:00
}
2020-04-23 18:33:35 +03:00
static int wled_ovp_fault_status ( struct wled * wled , bool * fault_set )
{
int rc ;
u32 int_rt_sts , fault_sts ;
* fault_set = false ;
rc = regmap_read ( wled - > regmap ,
wled - > ctrl_addr + WLED3_CTRL_REG_INT_RT_STS ,
& int_rt_sts ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " Failed to read INT_RT_STS rc=%d \n " , rc ) ;
return rc ;
}
rc = regmap_read ( wled - > regmap ,
wled - > ctrl_addr + WLED3_CTRL_REG_FAULT_STATUS ,
& fault_sts ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " Failed to read FAULT_STATUS rc=%d \n " , rc ) ;
return rc ;
}
if ( int_rt_sts & WLED3_CTRL_REG_OVP_FAULT_STATUS )
* fault_set = true ;
2020-04-23 18:33:37 +03:00
if ( wled - > version = = 4 & & ( fault_sts & WLED3_CTRL_REG_OVP_FAULT_BIT ) )
* fault_set = true ;
if ( wled - > version = = 5 & & ( fault_sts & ( WLED3_CTRL_REG_OVP_FAULT_BIT |
WLED5_CTRL_REG_OVP_PRE_ALARM_BIT ) ) )
2020-04-23 18:33:35 +03:00
* fault_set = true ;
if ( * fault_set )
dev_dbg ( wled - > dev , " WLED OVP fault detected, int_rt_sts=0x%x fault_sts=0x%x \n " ,
int_rt_sts , fault_sts ) ;
return rc ;
}
static int wled4_ovp_delay ( struct wled * wled )
{
return WLED_SOFT_START_DLY_US ;
}
2020-04-23 18:33:37 +03:00
static int wled5_ovp_delay ( struct wled * wled )
{
int rc , delay_us ;
u32 val ;
u8 ovp_timer_ms [ 8 ] = { 1 , 2 , 4 , 8 , 12 , 16 , 20 , 24 } ;
/* For WLED5, get the delay based on OVP timer */
rc = regmap_read ( wled - > regmap , wled - > ctrl_addr +
WLED5_CTRL_REG_OVP_INT_CTL , & val ) ;
if ( rc < 0 )
delay_us =
ovp_timer_ms [ val & WLED5_CTRL_REG_OVP_INT_TIMER_MASK ] * 1000 ;
else
delay_us = 2 * WLED_SOFT_START_DLY_US ;
dev_dbg ( wled - > dev , " delay_time_us: %d \n " , delay_us ) ;
return delay_us ;
}
2019-11-01 09:27:00 +03:00
static int wled_update_status ( struct backlight_device * bl )
2015-03-12 18:47:08 +03:00
{
2019-11-01 09:27:00 +03:00
struct wled * wled = bl_get_data ( bl ) ;
2020-07-19 11:07:41 +03:00
u16 brightness = backlight_get_brightness ( bl ) ;
2019-11-01 09:27:01 +03:00
int rc = 0 ;
2015-03-12 18:47:08 +03:00
2019-11-01 09:27:03 +03:00
mutex_lock ( & wled - > lock ) ;
2019-11-01 09:27:01 +03:00
if ( brightness ) {
rc = wled - > wled_set_brightness ( wled , brightness ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " wled failed to set brightness rc:%d \n " ,
rc ) ;
2019-11-01 09:27:03 +03:00
goto unlock_mutex ;
2019-11-01 09:27:01 +03:00
}
2015-03-12 18:47:08 +03:00
2021-03-18 15:39:39 +03:00
if ( wled - > version < 5 ) {
rc = wled - > wled_sync_toggle ( wled ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " wled sync failed rc:%d \n " , rc ) ;
goto unlock_mutex ;
}
} else {
/*
* For WLED5 toggling the MOD_SYNC_BIT updates the
* brightness
*/
rc = wled5_mod_sync_toggle ( wled ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " wled mod sync failed rc:%d \n " ,
rc ) ;
goto unlock_mutex ;
}
2019-11-01 09:27:01 +03:00
}
}
2015-03-12 18:47:08 +03:00
2019-11-01 09:27:01 +03:00
if ( ! ! brightness ! = ! ! wled - > brightness ) {
rc = wled_module_enable ( wled , ! ! brightness ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " wled enable failed rc:%d \n " , rc ) ;
2019-11-01 09:27:03 +03:00
goto unlock_mutex ;
2019-11-01 09:27:01 +03:00
}
2015-03-12 18:47:08 +03:00
}
2019-11-01 09:27:01 +03:00
wled - > brightness = brightness ;
2015-03-12 18:47:08 +03:00
2019-11-01 09:27:03 +03:00
unlock_mutex :
mutex_unlock ( & wled - > lock ) ;
2015-03-12 18:47:08 +03:00
return rc ;
}
2020-04-23 18:33:35 +03:00
static int wled4_cabc_config ( struct wled * wled , bool enable )
{
int i , j , rc ;
u8 val ;
for ( i = 0 ; i < wled - > cfg . num_strings ; i + + ) {
j = wled - > cfg . enabled_strings [ i ] ;
val = enable ? WLED4_SINK_REG_STR_CABC_MASK : 0 ;
rc = regmap_update_bits ( wled - > regmap , wled - > sink_addr +
WLED4_SINK_REG_STR_CABC ( j ) ,
WLED4_SINK_REG_STR_CABC_MASK , val ) ;
if ( rc < 0 )
return rc ;
}
return 0 ;
}
2020-04-23 18:33:37 +03:00
static int wled5_cabc_config ( struct wled * wled , bool enable )
{
int rc , offset ;
u8 reg ;
if ( wled - > cabc_disabled )
return 0 ;
reg = enable ? wled - > cfg . cabc_sel : 0 ;
offset = ( wled - > cfg . mod_sel = = MOD_A ) ? WLED5_SINK_REG_MOD_A_SRC_SEL :
WLED5_SINK_REG_MOD_B_SRC_SEL ;
rc = regmap_update_bits ( wled - > regmap , wled - > sink_addr + offset ,
WLED5_SINK_REG_MOD_SRC_SEL_MASK , reg ) ;
if ( rc < 0 ) {
pr_err ( " Error in configuring CABC rc=%d \n " , rc ) ;
return rc ;
}
if ( ! wled - > cfg . cabc_sel )
wled - > cabc_disabled = true ;
return 0 ;
}
2019-11-01 09:27:03 +03:00
# define WLED_SHORT_DLY_MS 20
# define WLED_SHORT_CNT_MAX 5
# define WLED_SHORT_RESET_CNT_DLY_US USEC_PER_SEC
static irqreturn_t wled_short_irq_handler ( int irq , void * _wled )
{
struct wled * wled = _wled ;
int rc ;
s64 elapsed_time ;
wled - > short_count + + ;
mutex_lock ( & wled - > lock ) ;
rc = wled_module_enable ( wled , false ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " wled disable failed rc:%d \n " , rc ) ;
goto unlock_mutex ;
}
elapsed_time = ktime_us_delta ( ktime_get ( ) ,
wled - > last_short_event ) ;
if ( elapsed_time > WLED_SHORT_RESET_CNT_DLY_US )
wled - > short_count = 1 ;
if ( wled - > short_count > WLED_SHORT_CNT_MAX ) {
2019-11-12 12:30:25 +03:00
dev_err ( wled - > dev , " Short triggered %d times, disabling WLED forever! \n " ,
2019-11-01 09:27:03 +03:00
wled - > short_count ) ;
wled - > disabled_by_short = true ;
goto unlock_mutex ;
}
wled - > last_short_event = ktime_get ( ) ;
msleep ( WLED_SHORT_DLY_MS ) ;
rc = wled_module_enable ( wled , true ) ;
if ( rc < 0 )
dev_err ( wled - > dev , " wled enable failed rc:%d \n " , rc ) ;
unlock_mutex :
mutex_unlock ( & wled - > lock ) ;
return IRQ_HANDLED ;
}
2019-11-01 09:27:04 +03:00
# define AUTO_DETECT_BRIGHTNESS 200
static void wled_auto_string_detection ( struct wled * wled )
{
2021-11-15 23:34:59 +03:00
int rc = 0 , i , j , delay_time_us ;
2020-04-23 18:33:35 +03:00
u32 sink_config = 0 ;
2019-11-01 09:27:04 +03:00
u8 sink_test = 0 , sink_valid = 0 , val ;
2020-04-23 18:33:35 +03:00
bool fault_set ;
2019-11-01 09:27:04 +03:00
/* Read configured sink configuration */
rc = regmap_read ( wled - > regmap , wled - > sink_addr +
WLED4_SINK_REG_CURR_SINK , & sink_config ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " Failed to read SINK configuration rc=%d \n " ,
rc ) ;
goto failed_detect ;
}
/* Disable the module before starting detection */
rc = regmap_update_bits ( wled - > regmap ,
wled - > ctrl_addr + WLED3_CTRL_REG_MOD_EN ,
WLED3_CTRL_REG_MOD_EN_MASK , 0 ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " Failed to disable WLED module rc=%d \n " , rc ) ;
goto failed_detect ;
}
/* Set low brightness across all sinks */
rc = wled4_set_brightness ( wled , AUTO_DETECT_BRIGHTNESS ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " Failed to set brightness for auto detection rc=%d \n " ,
rc ) ;
goto failed_detect ;
}
if ( wled - > cfg . cabc ) {
2020-04-23 18:33:35 +03:00
rc = wled - > wled_cabc_config ( wled , false ) ;
if ( rc < 0 )
goto failed_detect ;
2019-11-01 09:27:04 +03:00
}
/* Disable all sinks */
rc = regmap_write ( wled - > regmap ,
wled - > sink_addr + WLED4_SINK_REG_CURR_SINK , 0 ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " Failed to disable all sinks rc=%d \n " , rc ) ;
goto failed_detect ;
}
/* Iterate through the strings one by one */
for ( i = 0 ; i < wled - > cfg . num_strings ; i + + ) {
2021-11-15 23:34:59 +03:00
j = wled - > cfg . enabled_strings [ i ] ;
sink_test = BIT ( ( WLED4_SINK_REG_CURR_SINK_SHFT + j ) ) ;
2019-11-01 09:27:04 +03:00
/* Enable feedback control */
rc = regmap_write ( wled - > regmap , wled - > ctrl_addr +
2021-11-15 23:34:59 +03:00
WLED3_CTRL_REG_FEEDBACK_CONTROL , j + 1 ) ;
2019-11-01 09:27:04 +03:00
if ( rc < 0 ) {
dev_err ( wled - > dev , " Failed to enable feedback for SINK %d rc = %d \n " ,
2021-11-15 23:34:59 +03:00
j + 1 , rc ) ;
2019-11-01 09:27:04 +03:00
goto failed_detect ;
}
/* Enable the sink */
rc = regmap_write ( wled - > regmap , wled - > sink_addr +
WLED4_SINK_REG_CURR_SINK , sink_test ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " Failed to configure SINK %d rc=%d \n " ,
2021-11-15 23:34:59 +03:00
j + 1 , rc ) ;
2019-11-01 09:27:04 +03:00
goto failed_detect ;
}
/* Enable the module */
rc = regmap_update_bits ( wled - > regmap , wled - > ctrl_addr +
WLED3_CTRL_REG_MOD_EN ,
WLED3_CTRL_REG_MOD_EN_MASK ,
WLED3_CTRL_REG_MOD_EN_MASK ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " Failed to enable WLED module rc=%d \n " ,
rc ) ;
goto failed_detect ;
}
2020-04-23 18:33:35 +03:00
delay_time_us = wled - > wled_ovp_delay ( wled ) ;
usleep_range ( delay_time_us , delay_time_us + 1000 ) ;
2019-11-01 09:27:04 +03:00
2020-04-23 18:33:35 +03:00
rc = wled_ovp_fault_status ( wled , & fault_set ) ;
2019-11-01 09:27:04 +03:00
if ( rc < 0 ) {
2020-04-23 18:33:35 +03:00
dev_err ( wled - > dev , " Error in getting OVP fault_sts, rc=%d \n " ,
2019-11-01 09:27:04 +03:00
rc ) ;
goto failed_detect ;
}
2020-04-23 18:33:35 +03:00
if ( fault_set )
2019-11-01 09:27:04 +03:00
dev_dbg ( wled - > dev , " WLED OVP fault detected with SINK %d \n " ,
2021-11-15 23:34:59 +03:00
j + 1 ) ;
2019-11-01 09:27:04 +03:00
else
sink_valid | = sink_test ;
/* Disable the module */
rc = regmap_update_bits ( wled - > regmap ,
wled - > ctrl_addr + WLED3_CTRL_REG_MOD_EN ,
WLED3_CTRL_REG_MOD_EN_MASK , 0 ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " Failed to disable WLED module rc=%d \n " ,
rc ) ;
goto failed_detect ;
}
}
if ( ! sink_valid ) {
dev_err ( wled - > dev , " No valid WLED sinks found \n " ) ;
wled - > disabled_by_short = true ;
goto failed_detect ;
}
if ( sink_valid ! = sink_config ) {
dev_warn ( wled - > dev , " %x is not a valid sink configuration - using %x instead \n " ,
sink_config , sink_valid ) ;
sink_config = sink_valid ;
}
/* Write the new sink configuration */
rc = regmap_write ( wled - > regmap ,
wled - > sink_addr + WLED4_SINK_REG_CURR_SINK ,
sink_config ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " Failed to reconfigure the default sink rc=%d \n " ,
rc ) ;
goto failed_detect ;
}
/* Enable valid sinks */
2020-04-23 18:33:35 +03:00
if ( wled - > version = = 4 ) {
for ( i = 0 ; i < wled - > cfg . num_strings ; i + + ) {
2021-11-15 23:34:59 +03:00
j = wled - > cfg . enabled_strings [ i ] ;
2020-04-23 18:33:35 +03:00
if ( sink_config &
2021-11-15 23:34:59 +03:00
BIT ( WLED4_SINK_REG_CURR_SINK_SHFT + j ) )
2020-04-23 18:33:35 +03:00
val = WLED4_SINK_REG_STR_MOD_MASK ;
else
/* Disable modulator_en for unused sink */
val = 0 ;
rc = regmap_write ( wled - > regmap , wled - > sink_addr +
2021-11-15 23:34:59 +03:00
WLED4_SINK_REG_STR_MOD_EN ( j ) , val ) ;
2020-04-23 18:33:35 +03:00
if ( rc < 0 ) {
dev_err ( wled - > dev , " Failed to configure MODULATOR_EN rc=%d \n " ,
rc ) ;
2019-11-01 09:27:04 +03:00
goto failed_detect ;
2020-04-23 18:33:35 +03:00
}
2019-11-01 09:27:04 +03:00
}
}
2020-04-23 18:33:35 +03:00
/* Enable CABC */
rc = wled - > wled_cabc_config ( wled , true ) ;
if ( rc < 0 )
goto failed_detect ;
2019-11-01 09:27:04 +03:00
/* Restore the feedback setting */
rc = regmap_write ( wled - > regmap ,
wled - > ctrl_addr + WLED3_CTRL_REG_FEEDBACK_CONTROL , 0 ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " Failed to restore feedback setting rc=%d \n " ,
rc ) ;
goto failed_detect ;
}
/* Restore brightness */
rc = wled4_set_brightness ( wled , wled - > brightness ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " Failed to set brightness after auto detection rc=%d \n " ,
rc ) ;
goto failed_detect ;
}
rc = regmap_update_bits ( wled - > regmap ,
wled - > ctrl_addr + WLED3_CTRL_REG_MOD_EN ,
WLED3_CTRL_REG_MOD_EN_MASK ,
WLED3_CTRL_REG_MOD_EN_MASK ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " Failed to enable WLED module rc=%d \n " , rc ) ;
goto failed_detect ;
}
failed_detect :
return ;
}
# define WLED_AUTO_DETECT_OVP_COUNT 5
# define WLED_AUTO_DETECT_CNT_DLY_US USEC_PER_SEC
2020-04-23 18:33:35 +03:00
static bool wled4_auto_detection_required ( struct wled * wled )
2019-11-01 09:27:04 +03:00
{
s64 elapsed_time_us ;
if ( ! wled - > cfg . auto_detection_enabled )
return false ;
/*
* Check if the OVP fault was an occasional one
* or if it ' s firing continuously , the latter qualifies
* for an auto - detection check .
*/
if ( ! wled - > auto_detection_ovp_count ) {
wled - > start_ovp_fault_time = ktime_get ( ) ;
wled - > auto_detection_ovp_count + + ;
} else {
elapsed_time_us = ktime_us_delta ( ktime_get ( ) ,
wled - > start_ovp_fault_time ) ;
if ( elapsed_time_us > WLED_AUTO_DETECT_CNT_DLY_US )
wled - > auto_detection_ovp_count = 0 ;
else
wled - > auto_detection_ovp_count + + ;
if ( wled - > auto_detection_ovp_count > =
WLED_AUTO_DETECT_OVP_COUNT ) {
wled - > auto_detection_ovp_count = 0 ;
return true ;
}
}
return false ;
}
2020-04-23 18:33:37 +03:00
static bool wled5_auto_detection_required ( struct wled * wled )
{
if ( ! wled - > cfg . auto_detection_enabled )
return false ;
/*
* Unlike WLED4 , WLED5 has OVP fault density interrupt configuration
* i . e . to count the number of OVP alarms for a certain duration before
* triggering OVP fault interrupt . By default , number of OVP fault
* events counted before an interrupt is fired is 32 and the time
* interval is 12 ms . If we see one OVP fault interrupt , then that
* should qualify for a real OVP fault condition to run auto detection
* algorithm .
*/
return true ;
}
2019-11-01 09:27:04 +03:00
static int wled_auto_detection_at_init ( struct wled * wled )
{
int rc ;
2020-04-23 18:33:35 +03:00
bool fault_set ;
2019-11-01 09:27:04 +03:00
if ( ! wled - > cfg . auto_detection_enabled )
return 0 ;
2020-04-23 18:33:35 +03:00
rc = wled_ovp_fault_status ( wled , & fault_set ) ;
2019-11-01 09:27:04 +03:00
if ( rc < 0 ) {
2020-04-23 18:33:35 +03:00
dev_err ( wled - > dev , " Error in getting OVP fault_sts, rc=%d \n " ,
rc ) ;
2019-11-01 09:27:04 +03:00
return rc ;
}
2020-04-23 18:33:35 +03:00
if ( fault_set ) {
2019-11-01 09:27:04 +03:00
mutex_lock ( & wled - > lock ) ;
wled_auto_string_detection ( wled ) ;
mutex_unlock ( & wled - > lock ) ;
}
return rc ;
}
static irqreturn_t wled_ovp_irq_handler ( int irq , void * _wled )
{
struct wled * wled = _wled ;
int rc ;
u32 int_sts , fault_sts ;
rc = regmap_read ( wled - > regmap ,
wled - > ctrl_addr + WLED3_CTRL_REG_INT_RT_STS , & int_sts ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " Error in reading WLED3_INT_RT_STS rc=%d \n " ,
rc ) ;
return IRQ_HANDLED ;
}
rc = regmap_read ( wled - > regmap , wled - > ctrl_addr +
WLED3_CTRL_REG_FAULT_STATUS , & fault_sts ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " Error in reading WLED_FAULT_STATUS rc=%d \n " ,
rc ) ;
return IRQ_HANDLED ;
}
if ( fault_sts & ( WLED3_CTRL_REG_OVP_FAULT_BIT |
WLED3_CTRL_REG_ILIM_FAULT_BIT ) )
dev_dbg ( wled - > dev , " WLED OVP fault detected, int_sts=%x fault_sts= %x \n " ,
int_sts , fault_sts ) ;
if ( fault_sts & WLED3_CTRL_REG_OVP_FAULT_BIT ) {
2020-04-23 18:33:35 +03:00
if ( wled - > wled_auto_detection_required ( wled ) ) {
2019-11-01 09:27:04 +03:00
mutex_lock ( & wled - > lock ) ;
wled_auto_string_detection ( wled ) ;
mutex_unlock ( & wled - > lock ) ;
}
}
return IRQ_HANDLED ;
}
2019-11-01 09:27:01 +03:00
static int wled3_setup ( struct wled * wled )
2015-03-12 18:47:08 +03:00
{
2019-11-01 09:27:01 +03:00
u16 addr ;
u8 sink_en = 0 ;
int rc , i , j ;
2015-03-12 18:47:08 +03:00
rc = regmap_update_bits ( wled - > regmap ,
2019-11-01 09:27:01 +03:00
wled - > ctrl_addr + WLED3_CTRL_REG_OVP ,
WLED3_CTRL_REG_OVP_MASK , wled - > cfg . ovp ) ;
2015-03-12 18:47:08 +03:00
if ( rc )
return rc ;
rc = regmap_update_bits ( wled - > regmap ,
2019-11-01 09:27:01 +03:00
wled - > ctrl_addr + WLED3_CTRL_REG_ILIMIT ,
WLED3_CTRL_REG_ILIMIT_MASK ,
wled - > cfg . boost_i_limit ) ;
2015-03-12 18:47:08 +03:00
if ( rc )
return rc ;
rc = regmap_update_bits ( wled - > regmap ,
2019-11-01 09:27:01 +03:00
wled - > ctrl_addr + WLED3_CTRL_REG_FREQ ,
WLED3_CTRL_REG_FREQ_MASK ,
wled - > cfg . switch_freq ) ;
2015-03-12 18:47:08 +03:00
if ( rc )
return rc ;
for ( i = 0 ; i < wled - > cfg . num_strings ; + + i ) {
2019-11-01 09:27:01 +03:00
j = wled - > cfg . enabled_strings [ i ] ;
addr = wled - > ctrl_addr + WLED3_SINK_REG_STR_MOD_EN ( j ) ;
rc = regmap_update_bits ( wled - > regmap , addr ,
WLED3_SINK_REG_STR_MOD_MASK ,
WLED3_SINK_REG_STR_MOD_MASK ) ;
2015-03-12 18:47:08 +03:00
if ( rc )
return rc ;
if ( wled - > cfg . ext_gen ) {
2019-11-01 09:27:01 +03:00
addr = wled - > ctrl_addr + WLED3_SINK_REG_STR_MOD_SRC ( j ) ;
rc = regmap_update_bits ( wled - > regmap , addr ,
WLED3_SINK_REG_STR_MOD_SRC_MASK ,
WLED3_SINK_REG_STR_MOD_SRC_EXT ) ;
2015-03-12 18:47:08 +03:00
if ( rc )
return rc ;
}
2019-11-01 09:27:01 +03:00
addr = wled - > ctrl_addr + WLED3_SINK_REG_STR_FULL_SCALE_CURR ( j ) ;
rc = regmap_update_bits ( wled - > regmap , addr ,
WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK ,
wled - > cfg . string_i_limit ) ;
2015-03-12 18:47:08 +03:00
if ( rc )
return rc ;
2019-11-01 09:27:01 +03:00
addr = wled - > ctrl_addr + WLED3_SINK_REG_STR_CABC ( j ) ;
rc = regmap_update_bits ( wled - > regmap , addr ,
WLED3_SINK_REG_STR_CABC_MASK ,
wled - > cfg . cabc ?
WLED3_SINK_REG_STR_CABC_MASK : 0 ) ;
2015-03-12 18:47:08 +03:00
if ( rc )
return rc ;
2019-11-01 09:27:01 +03:00
sink_en | = BIT ( j + WLED3_SINK_REG_CURR_SINK_SHFT ) ;
2015-03-12 18:47:08 +03:00
}
2019-11-01 09:27:01 +03:00
rc = regmap_update_bits ( wled - > regmap ,
wled - > ctrl_addr + WLED3_SINK_REG_CURR_SINK ,
WLED3_SINK_REG_CURR_SINK_MASK , sink_en ) ;
if ( rc )
return rc ;
2015-03-12 18:47:08 +03:00
return 0 ;
}
2019-11-01 09:27:00 +03:00
static const struct wled_config wled3_config_defaults = {
. boost_i_limit = 3 ,
. string_i_limit = 20 ,
2015-03-12 18:47:08 +03:00
. ovp = 2 ,
2019-11-01 09:27:01 +03:00
. num_strings = 3 ,
2015-03-12 18:47:08 +03:00
. switch_freq = 5 ,
. cs_out_en = false ,
. ext_gen = false ,
2019-11-01 09:27:01 +03:00
. cabc = false ,
2021-11-15 23:34:56 +03:00
. enabled_strings = { 0 , 1 , 2 } ,
2015-03-12 18:47:08 +03:00
} ;
2019-11-01 09:27:02 +03:00
static int wled4_setup ( struct wled * wled )
{
int rc , temp , i , j ;
u16 addr ;
u8 sink_en = 0 ;
2019-11-01 09:27:03 +03:00
u32 sink_cfg ;
2019-11-01 09:27:02 +03:00
rc = regmap_update_bits ( wled - > regmap ,
wled - > ctrl_addr + WLED3_CTRL_REG_OVP ,
WLED3_CTRL_REG_OVP_MASK , wled - > cfg . ovp ) ;
if ( rc < 0 )
return rc ;
rc = regmap_update_bits ( wled - > regmap ,
wled - > ctrl_addr + WLED3_CTRL_REG_ILIMIT ,
WLED3_CTRL_REG_ILIMIT_MASK ,
wled - > cfg . boost_i_limit ) ;
if ( rc < 0 )
return rc ;
rc = regmap_update_bits ( wled - > regmap ,
wled - > ctrl_addr + WLED3_CTRL_REG_FREQ ,
WLED3_CTRL_REG_FREQ_MASK ,
wled - > cfg . switch_freq ) ;
if ( rc < 0 )
return rc ;
2019-11-01 09:27:03 +03:00
if ( wled - > cfg . external_pfet ) {
/* Unlock the secure register access */
rc = regmap_write ( wled - > regmap , wled - > ctrl_addr +
WLED4_CTRL_REG_SEC_ACCESS ,
WLED4_CTRL_REG_SEC_UNLOCK ) ;
if ( rc < 0 )
return rc ;
rc = regmap_write ( wled - > regmap ,
wled - > ctrl_addr + WLED4_CTRL_REG_TEST1 ,
WLED4_CTRL_REG_TEST1_EXT_FET_DTEST2 ) ;
if ( rc < 0 )
return rc ;
}
2019-11-01 09:27:02 +03:00
rc = regmap_read ( wled - > regmap , wled - > sink_addr +
WLED4_SINK_REG_CURR_SINK , & sink_cfg ) ;
if ( rc < 0 )
return rc ;
for ( i = 0 ; i < wled - > cfg . num_strings ; i + + ) {
j = wled - > cfg . enabled_strings [ i ] ;
temp = j + WLED4_SINK_REG_CURR_SINK_SHFT ;
sink_en | = 1 < < temp ;
}
2019-11-01 09:27:04 +03:00
if ( sink_cfg = = sink_en ) {
rc = wled_auto_detection_at_init ( wled ) ;
return rc ;
}
2019-11-01 09:27:02 +03:00
rc = regmap_update_bits ( wled - > regmap ,
wled - > sink_addr + WLED4_SINK_REG_CURR_SINK ,
WLED4_SINK_REG_CURR_SINK_MASK , 0 ) ;
if ( rc < 0 )
return rc ;
rc = regmap_update_bits ( wled - > regmap , wled - > ctrl_addr +
WLED3_CTRL_REG_MOD_EN ,
WLED3_CTRL_REG_MOD_EN_MASK , 0 ) ;
if ( rc < 0 )
return rc ;
/* Per sink/string configuration */
for ( i = 0 ; i < wled - > cfg . num_strings ; i + + ) {
j = wled - > cfg . enabled_strings [ i ] ;
addr = wled - > sink_addr +
WLED4_SINK_REG_STR_MOD_EN ( j ) ;
rc = regmap_update_bits ( wled - > regmap , addr ,
WLED4_SINK_REG_STR_MOD_MASK ,
WLED4_SINK_REG_STR_MOD_MASK ) ;
if ( rc < 0 )
return rc ;
addr = wled - > sink_addr +
WLED4_SINK_REG_STR_FULL_SCALE_CURR ( j ) ;
rc = regmap_update_bits ( wled - > regmap , addr ,
WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK ,
wled - > cfg . string_i_limit ) ;
if ( rc < 0 )
return rc ;
}
2020-04-23 18:33:35 +03:00
rc = wled4_cabc_config ( wled , wled - > cfg . cabc ) ;
if ( rc < 0 )
return rc ;
2019-11-01 09:27:02 +03:00
rc = regmap_update_bits ( wled - > regmap , wled - > ctrl_addr +
WLED3_CTRL_REG_MOD_EN ,
WLED3_CTRL_REG_MOD_EN_MASK ,
WLED3_CTRL_REG_MOD_EN_MASK ) ;
if ( rc < 0 )
return rc ;
rc = regmap_update_bits ( wled - > regmap ,
wled - > sink_addr + WLED4_SINK_REG_CURR_SINK ,
WLED4_SINK_REG_CURR_SINK_MASK , sink_en ) ;
if ( rc < 0 )
return rc ;
2020-04-23 18:33:35 +03:00
rc = wled - > wled_sync_toggle ( wled ) ;
2019-11-01 09:27:02 +03:00
if ( rc < 0 ) {
dev_err ( wled - > dev , " Failed to toggle sync reg rc:%d \n " , rc ) ;
return rc ;
}
2019-11-01 09:27:04 +03:00
rc = wled_auto_detection_at_init ( wled ) ;
return rc ;
2019-11-01 09:27:02 +03:00
}
static const struct wled_config wled4_config_defaults = {
. boost_i_limit = 4 ,
. string_i_limit = 10 ,
. ovp = 1 ,
. num_strings = 4 ,
. switch_freq = 11 ,
. cabc = false ,
2019-11-01 09:27:03 +03:00
. external_pfet = false ,
2019-11-01 09:27:04 +03:00
. auto_detection_enabled = false ,
2021-11-15 23:34:57 +03:00
. enabled_strings = { 0 , 1 , 2 , 3 } ,
2019-11-01 09:27:02 +03:00
} ;
2020-04-23 18:33:37 +03:00
static int wled5_setup ( struct wled * wled )
{
int rc , temp , i , j , offset ;
u8 sink_en = 0 ;
u16 addr ;
u32 val ;
rc = regmap_update_bits ( wled - > regmap ,
wled - > ctrl_addr + WLED3_CTRL_REG_OVP ,
WLED5_CTRL_REG_OVP_MASK , wled - > cfg . ovp ) ;
if ( rc < 0 )
return rc ;
rc = regmap_update_bits ( wled - > regmap ,
wled - > ctrl_addr + WLED3_CTRL_REG_ILIMIT ,
WLED3_CTRL_REG_ILIMIT_MASK ,
wled - > cfg . boost_i_limit ) ;
if ( rc < 0 )
return rc ;
rc = regmap_update_bits ( wled - > regmap ,
wled - > ctrl_addr + WLED3_CTRL_REG_FREQ ,
WLED3_CTRL_REG_FREQ_MASK ,
wled - > cfg . switch_freq ) ;
if ( rc < 0 )
return rc ;
/* Per sink/string configuration */
for ( i = 0 ; i < wled - > cfg . num_strings ; + + i ) {
j = wled - > cfg . enabled_strings [ i ] ;
addr = wled - > sink_addr +
WLED4_SINK_REG_STR_FULL_SCALE_CURR ( j ) ;
rc = regmap_update_bits ( wled - > regmap , addr ,
WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK ,
wled - > cfg . string_i_limit ) ;
if ( rc < 0 )
return rc ;
addr = wled - > sink_addr + WLED5_SINK_REG_STR_SRC_SEL ( j ) ;
rc = regmap_update_bits ( wled - > regmap , addr ,
WLED5_SINK_REG_SRC_SEL_MASK ,
wled - > cfg . mod_sel = = MOD_A ?
WLED5_SINK_REG_SRC_SEL_MOD_A :
WLED5_SINK_REG_SRC_SEL_MOD_B ) ;
temp = j + WLED4_SINK_REG_CURR_SINK_SHFT ;
sink_en | = 1 < < temp ;
}
rc = wled5_cabc_config ( wled , wled - > cfg . cabc_sel ? true : false ) ;
if ( rc < 0 )
return rc ;
/* Enable one of the modulators A or B based on mod_sel */
addr = wled - > sink_addr + WLED5_SINK_REG_MOD_A_EN ;
val = ( wled - > cfg . mod_sel = = MOD_A ) ? WLED5_SINK_REG_MOD_EN_MASK : 0 ;
rc = regmap_update_bits ( wled - > regmap , addr ,
WLED5_SINK_REG_MOD_EN_MASK , val ) ;
if ( rc < 0 )
return rc ;
addr = wled - > sink_addr + WLED5_SINK_REG_MOD_B_EN ;
val = ( wled - > cfg . mod_sel = = MOD_B ) ? WLED5_SINK_REG_MOD_EN_MASK : 0 ;
rc = regmap_update_bits ( wled - > regmap , addr ,
WLED5_SINK_REG_MOD_EN_MASK , val ) ;
if ( rc < 0 )
return rc ;
offset = ( wled - > cfg . mod_sel = = MOD_A ) ?
WLED5_SINK_REG_MOD_A_BRIGHTNESS_WIDTH_SEL :
WLED5_SINK_REG_MOD_B_BRIGHTNESS_WIDTH_SEL ;
addr = wled - > sink_addr + offset ;
val = ( wled - > max_brightness = = WLED5_SINK_REG_BRIGHT_MAX_15B ) ?
WLED5_SINK_REG_BRIGHTNESS_WIDTH_15B :
WLED5_SINK_REG_BRIGHTNESS_WIDTH_12B ;
rc = regmap_write ( wled - > regmap , addr , val ) ;
if ( rc < 0 )
return rc ;
rc = regmap_update_bits ( wled - > regmap ,
wled - > sink_addr + WLED4_SINK_REG_CURR_SINK ,
WLED4_SINK_REG_CURR_SINK_MASK , sink_en ) ;
if ( rc < 0 )
return rc ;
/* This updates only FSC configuration in WLED5 */
rc = wled - > wled_sync_toggle ( wled ) ;
if ( rc < 0 ) {
pr_err ( " Failed to toggle sync reg rc:%d \n " , rc ) ;
return rc ;
}
rc = wled_auto_detection_at_init ( wled ) ;
if ( rc < 0 )
return rc ;
return 0 ;
}
static const struct wled_config wled5_config_defaults = {
. boost_i_limit = 5 ,
. string_i_limit = 10 ,
. ovp = 4 ,
. num_strings = 4 ,
. switch_freq = 11 ,
. mod_sel = 0 ,
. cabc_sel = 0 ,
. cabc = false ,
. external_pfet = false ,
. auto_detection_enabled = false ,
2021-11-15 23:34:57 +03:00
. enabled_strings = { 0 , 1 , 2 , 3 } ,
2020-04-23 18:33:37 +03:00
} ;
2019-11-01 09:27:00 +03:00
static const u32 wled3_boost_i_limit_values [ ] = {
2015-03-12 18:47:08 +03:00
105 , 385 , 525 , 805 , 980 , 1260 , 1400 , 1680 ,
} ;
2019-11-01 09:27:00 +03:00
static const struct wled_var_cfg wled3_boost_i_limit_cfg = {
. values = wled3_boost_i_limit_values ,
. size = ARRAY_SIZE ( wled3_boost_i_limit_values ) ,
2015-03-12 18:47:08 +03:00
} ;
2019-11-01 09:27:02 +03:00
static const u32 wled4_boost_i_limit_values [ ] = {
105 , 280 , 450 , 620 , 970 , 1150 , 1300 , 1500 ,
} ;
static const struct wled_var_cfg wled4_boost_i_limit_cfg = {
. values = wled4_boost_i_limit_values ,
. size = ARRAY_SIZE ( wled4_boost_i_limit_values ) ,
} ;
2020-04-23 18:33:37 +03:00
static inline u32 wled5_boost_i_limit_values_fn ( u32 idx )
{
return 525 + ( idx * 175 ) ;
}
static const struct wled_var_cfg wled5_boost_i_limit_cfg = {
. fn = wled5_boost_i_limit_values_fn ,
. size = 8 ,
} ;
2019-11-01 09:27:00 +03:00
static const u32 wled3_ovp_values [ ] = {
2015-03-12 18:47:08 +03:00
35 , 32 , 29 , 27 ,
} ;
2019-11-01 09:27:00 +03:00
static const struct wled_var_cfg wled3_ovp_cfg = {
. values = wled3_ovp_values ,
. size = ARRAY_SIZE ( wled3_ovp_values ) ,
2015-03-12 18:47:08 +03:00
} ;
2019-11-01 09:27:02 +03:00
static const u32 wled4_ovp_values [ ] = {
31100 , 29600 , 19600 , 18100 ,
} ;
static const struct wled_var_cfg wled4_ovp_cfg = {
. values = wled4_ovp_values ,
. size = ARRAY_SIZE ( wled4_ovp_values ) ,
} ;
2020-04-23 18:33:37 +03:00
static inline u32 wled5_ovp_values_fn ( u32 idx )
{
/*
* 0000 - 38.5 V
* 0001 - 37 V . .
* 1111 - 16 V
*/
return 38500 - ( idx * 1500 ) ;
}
static const struct wled_var_cfg wled5_ovp_cfg = {
. fn = wled5_ovp_values_fn ,
. size = 16 ,
} ;
2019-11-01 09:27:00 +03:00
static u32 wled3_switch_freq_values_fn ( u32 idx )
2015-03-12 18:47:08 +03:00
{
return 19200 / ( 2 * ( 1 + idx ) ) ;
}
2019-11-01 09:27:00 +03:00
static const struct wled_var_cfg wled3_switch_freq_cfg = {
. fn = wled3_switch_freq_values_fn ,
2015-03-12 18:47:08 +03:00
. size = 16 ,
} ;
2019-11-01 09:27:00 +03:00
static const struct wled_var_cfg wled3_string_i_limit_cfg = {
2015-03-12 18:47:08 +03:00
. size = 26 ,
} ;
2019-11-01 09:27:02 +03:00
static const u32 wled4_string_i_limit_values [ ] = {
0 , 2500 , 5000 , 7500 , 10000 , 12500 , 15000 , 17500 , 20000 ,
22500 , 25000 , 27500 , 30000 ,
} ;
static const struct wled_var_cfg wled4_string_i_limit_cfg = {
. values = wled4_string_i_limit_values ,
. size = ARRAY_SIZE ( wled4_string_i_limit_values ) ,
} ;
2020-04-23 18:33:37 +03:00
static const struct wled_var_cfg wled5_mod_sel_cfg = {
. size = 2 ,
} ;
static const struct wled_var_cfg wled5_cabc_sel_cfg = {
. size = 4 ,
} ;
2019-11-01 09:27:01 +03:00
static u32 wled_values ( const struct wled_var_cfg * cfg , u32 idx )
2015-03-12 18:47:08 +03:00
{
if ( idx > = cfg - > size )
return UINT_MAX ;
if ( cfg - > fn )
return cfg - > fn ( idx ) ;
if ( cfg - > values )
return cfg - > values [ idx ] ;
return idx ;
}
2020-04-23 18:33:35 +03:00
static int wled_configure ( struct wled * wled )
2015-03-12 18:47:08 +03:00
{
2019-11-01 09:27:00 +03:00
struct wled_config * cfg = & wled - > cfg ;
2019-11-01 09:27:01 +03:00
struct device * dev = wled - > dev ;
const __be32 * prop_addr ;
2020-01-22 04:32:40 +03:00
u32 size , val , c ;
int rc , i , j , string_len ;
2019-11-01 09:27:01 +03:00
const struct wled_u32_opts * u32_opts = NULL ;
const struct wled_u32_opts wled3_opts [ ] = {
2015-03-12 18:47:08 +03:00
{
2019-11-01 09:27:01 +03:00
. name = " qcom,current-boost-limit " ,
. val_ptr = & cfg - > boost_i_limit ,
2019-11-01 09:27:00 +03:00
. cfg = & wled3_boost_i_limit_cfg ,
2015-03-12 18:47:08 +03:00
} ,
{
2019-11-01 09:27:01 +03:00
. name = " qcom,current-limit " ,
. val_ptr = & cfg - > string_i_limit ,
2019-11-01 09:27:00 +03:00
. cfg = & wled3_string_i_limit_cfg ,
2015-03-12 18:47:08 +03:00
} ,
{
2019-11-01 09:27:01 +03:00
. name = " qcom,ovp " ,
. val_ptr = & cfg - > ovp ,
2019-11-01 09:27:00 +03:00
. cfg = & wled3_ovp_cfg ,
2015-03-12 18:47:08 +03:00
} ,
{
2019-11-01 09:27:01 +03:00
. name = " qcom,switching-freq " ,
. val_ptr = & cfg - > switch_freq ,
2019-11-01 09:27:00 +03:00
. cfg = & wled3_switch_freq_cfg ,
2015-03-12 18:47:08 +03:00
} ,
} ;
2019-11-01 09:27:01 +03:00
2019-11-01 09:27:02 +03:00
const struct wled_u32_opts wled4_opts [ ] = {
{
. name = " qcom,current-boost-limit " ,
. val_ptr = & cfg - > boost_i_limit ,
. cfg = & wled4_boost_i_limit_cfg ,
} ,
{
. name = " qcom,current-limit-microamp " ,
. val_ptr = & cfg - > string_i_limit ,
. cfg = & wled4_string_i_limit_cfg ,
} ,
{
. name = " qcom,ovp-millivolt " ,
. val_ptr = & cfg - > ovp ,
. cfg = & wled4_ovp_cfg ,
} ,
{
. name = " qcom,switching-freq " ,
. val_ptr = & cfg - > switch_freq ,
. cfg = & wled3_switch_freq_cfg ,
} ,
} ;
2020-04-23 18:33:37 +03:00
const struct wled_u32_opts wled5_opts [ ] = {
{
. name = " qcom,current-boost-limit " ,
. val_ptr = & cfg - > boost_i_limit ,
. cfg = & wled5_boost_i_limit_cfg ,
} ,
{
. name = " qcom,current-limit-microamp " ,
. val_ptr = & cfg - > string_i_limit ,
. cfg = & wled4_string_i_limit_cfg ,
} ,
{
. name = " qcom,ovp-millivolt " ,
. val_ptr = & cfg - > ovp ,
. cfg = & wled5_ovp_cfg ,
} ,
{
. name = " qcom,switching-freq " ,
. val_ptr = & cfg - > switch_freq ,
. cfg = & wled3_switch_freq_cfg ,
} ,
{
. name = " qcom,modulator-sel " ,
. val_ptr = & cfg - > mod_sel ,
. cfg = & wled5_mod_sel_cfg ,
} ,
{
. name = " qcom,cabc-sel " ,
. val_ptr = & cfg - > cabc_sel ,
. cfg = & wled5_cabc_sel_cfg ,
} ,
} ;
2019-11-01 09:27:01 +03:00
const struct wled_bool_opts bool_opts [ ] = {
2015-03-12 18:47:08 +03:00
{ " qcom,cs-out " , & cfg - > cs_out_en , } ,
{ " qcom,ext-gen " , & cfg - > ext_gen , } ,
2019-11-01 09:27:01 +03:00
{ " qcom,cabc " , & cfg - > cabc , } ,
2019-11-01 09:27:03 +03:00
{ " qcom,external-pfet " , & cfg - > external_pfet , } ,
2019-11-01 09:27:04 +03:00
{ " qcom,auto-string-detection " , & cfg - > auto_detection_enabled , } ,
2015-03-12 18:47:08 +03:00
} ;
2019-11-01 09:27:01 +03:00
prop_addr = of_get_address ( dev - > of_node , 0 , NULL , NULL ) ;
if ( ! prop_addr ) {
dev_err ( wled - > dev , " invalid IO resources \n " ) ;
return - EINVAL ;
2015-03-12 18:47:08 +03:00
}
2019-11-01 09:27:01 +03:00
wled - > ctrl_addr = be32_to_cpu ( * prop_addr ) ;
2015-03-12 18:47:08 +03:00
2015-07-22 03:44:49 +03:00
rc = of_property_read_string ( dev - > of_node , " label " , & wled - > name ) ;
2015-03-12 18:47:08 +03:00
if ( rc )
2018-08-28 04:03:42 +03:00
wled - > name = devm_kasprintf ( dev , GFP_KERNEL , " %pOFn " , dev - > of_node ) ;
2015-03-12 18:47:08 +03:00
2020-04-23 18:33:35 +03:00
switch ( wled - > version ) {
2019-11-01 09:27:01 +03:00
case 3 :
u32_opts = wled3_opts ;
size = ARRAY_SIZE ( wled3_opts ) ;
* cfg = wled3_config_defaults ;
wled - > wled_set_brightness = wled3_set_brightness ;
2020-04-23 18:33:35 +03:00
wled - > wled_sync_toggle = wled3_sync_toggle ;
2019-11-01 09:27:01 +03:00
wled - > max_string_count = 3 ;
2019-11-01 09:27:02 +03:00
wled - > sink_addr = wled - > ctrl_addr ;
break ;
case 4 :
u32_opts = wled4_opts ;
size = ARRAY_SIZE ( wled4_opts ) ;
* cfg = wled4_config_defaults ;
wled - > wled_set_brightness = wled4_set_brightness ;
2020-04-23 18:33:35 +03:00
wled - > wled_sync_toggle = wled3_sync_toggle ;
wled - > wled_cabc_config = wled4_cabc_config ;
wled - > wled_ovp_delay = wled4_ovp_delay ;
wled - > wled_auto_detection_required =
wled4_auto_detection_required ;
2019-11-01 09:27:02 +03:00
wled - > max_string_count = 4 ;
prop_addr = of_get_address ( dev - > of_node , 1 , NULL , NULL ) ;
if ( ! prop_addr ) {
dev_err ( wled - > dev , " invalid IO resources \n " ) ;
return - EINVAL ;
}
wled - > sink_addr = be32_to_cpu ( * prop_addr ) ;
2019-11-01 09:27:01 +03:00
break ;
2020-04-23 18:33:37 +03:00
case 5 :
u32_opts = wled5_opts ;
size = ARRAY_SIZE ( wled5_opts ) ;
* cfg = wled5_config_defaults ;
wled - > wled_set_brightness = wled5_set_brightness ;
2021-03-18 15:39:39 +03:00
wled - > wled_sync_toggle = wled3_sync_toggle ;
2020-04-23 18:33:37 +03:00
wled - > wled_cabc_config = wled5_cabc_config ;
wled - > wled_ovp_delay = wled5_ovp_delay ;
wled - > wled_auto_detection_required =
wled5_auto_detection_required ;
wled - > max_string_count = 4 ;
prop_addr = of_get_address ( dev - > of_node , 1 , NULL , NULL ) ;
if ( ! prop_addr ) {
dev_err ( wled - > dev , " invalid IO resources \n " ) ;
return - EINVAL ;
}
wled - > sink_addr = be32_to_cpu ( * prop_addr ) ;
break ;
2019-11-01 09:27:01 +03:00
default :
dev_err ( wled - > dev , " Invalid WLED version \n " ) ;
return - EINVAL ;
}
for ( i = 0 ; i < size ; + + i ) {
2015-03-12 18:47:08 +03:00
rc = of_property_read_u32 ( dev - > of_node , u32_opts [ i ] . name , & val ) ;
if ( rc = = - EINVAL ) {
continue ;
} else if ( rc ) {
dev_err ( dev , " error reading '%s' \n " , u32_opts [ i ] . name ) ;
return rc ;
}
c = UINT_MAX ;
for ( j = 0 ; c ! = val ; j + + ) {
2019-11-01 09:27:01 +03:00
c = wled_values ( u32_opts [ i ] . cfg , j ) ;
2015-03-12 18:47:08 +03:00
if ( c = = UINT_MAX ) {
dev_err ( dev , " invalid value for '%s' \n " ,
u32_opts [ i ] . name ) ;
return - EINVAL ;
}
2019-11-01 09:27:01 +03:00
if ( c = = val )
break ;
2015-03-12 18:47:08 +03:00
}
dev_dbg ( dev , " '%s' = %u \n " , u32_opts [ i ] . name , c ) ;
* u32_opts [ i ] . val_ptr = j ;
}
for ( i = 0 ; i < ARRAY_SIZE ( bool_opts ) ; + + i ) {
if ( of_property_read_bool ( dev - > of_node , bool_opts [ i ] . name ) )
* bool_opts [ i ] . val_ptr = true ;
}
2019-11-01 09:27:01 +03:00
string_len = of_property_count_elems_of_size ( dev - > of_node ,
" qcom,enabled-strings " ,
sizeof ( u32 ) ) ;
2021-11-15 23:34:51 +03:00
if ( string_len > 0 ) {
if ( string_len > wled - > max_string_count ) {
dev_err ( dev , " Cannot have more than %d strings \n " ,
wled - > max_string_count ) ;
return - EINVAL ;
}
2021-11-15 23:34:52 +03:00
rc = of_property_read_u32_array ( dev - > of_node ,
2019-11-01 09:27:01 +03:00
" qcom,enabled-strings " ,
wled - > cfg . enabled_strings ,
2021-11-15 23:34:52 +03:00
string_len ) ;
if ( rc ) {
dev_err ( dev , " Failed to read %d elements from qcom,enabled-strings: %d \n " ,
string_len , rc ) ;
return rc ;
}
2019-11-01 09:27:01 +03:00
2021-11-15 23:34:51 +03:00
for ( i = 0 ; i < string_len ; + + i ) {
if ( wled - > cfg . enabled_strings [ i ] > = wled - > max_string_count ) {
dev_err ( dev ,
" qcom,enabled-strings index %d at %d is out of bounds \n " ,
wled - > cfg . enabled_strings [ i ] , i ) ;
return - EINVAL ;
}
}
backlight: qcom-wled: Override default length with qcom,enabled-strings
The length of qcom,enabled-strings as property array is enough to
determine the number of strings to be enabled, without needing to set
qcom,num-strings to override the default number of strings when less
than the default (which is also the maximum) is provided in DT.
This also introduces an extra warning when qcom,num-strings is set,
denoting that it is not necessary to set both anymore. It is usually
more concise to set just qcom,num-length when a zero-based, contiguous
range of strings is needed (the majority of the cases), or to only set
qcom,enabled-strings when a specific set of indices is desired.
Fixes: 775d2ffb4af6 ("backlight: qcom-wled: Restructure the driver for WLED3")
Signed-off-by: Marijn Suijten <marijn.suijten@somainline.org>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Link: https://lore.kernel.org/r/20211115203459.1634079-6-marijn.suijten@somainline.org
2021-11-15 23:34:55 +03:00
cfg - > num_strings = string_len ;
2021-11-15 23:34:51 +03:00
}
2021-11-15 23:34:54 +03:00
rc = of_property_read_u32 ( dev - > of_node , " qcom,num-strings " , & val ) ;
if ( ! rc ) {
if ( val < 1 | | val > wled - > max_string_count ) {
dev_err ( dev , " qcom,num-strings must be between 1 and %d \n " ,
wled - > max_string_count ) ;
return - EINVAL ;
}
backlight: qcom-wled: Override default length with qcom,enabled-strings
The length of qcom,enabled-strings as property array is enough to
determine the number of strings to be enabled, without needing to set
qcom,num-strings to override the default number of strings when less
than the default (which is also the maximum) is provided in DT.
This also introduces an extra warning when qcom,num-strings is set,
denoting that it is not necessary to set both anymore. It is usually
more concise to set just qcom,num-length when a zero-based, contiguous
range of strings is needed (the majority of the cases), or to only set
qcom,enabled-strings when a specific set of indices is desired.
Fixes: 775d2ffb4af6 ("backlight: qcom-wled: Restructure the driver for WLED3")
Signed-off-by: Marijn Suijten <marijn.suijten@somainline.org>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Link: https://lore.kernel.org/r/20211115203459.1634079-6-marijn.suijten@somainline.org
2021-11-15 23:34:55 +03:00
if ( string_len > 0 ) {
dev_warn ( dev , " Only one of qcom,num-strings or qcom,enabled-strings "
" should be set \n " ) ;
if ( val > string_len ) {
dev_err ( dev , " qcom,num-strings exceeds qcom,enabled-strings \n " ) ;
return - EINVAL ;
}
2021-11-15 23:34:54 +03:00
}
cfg - > num_strings = val ;
}
2015-03-12 18:47:08 +03:00
return 0 ;
}
2019-11-01 09:27:03 +03:00
static int wled_configure_short_irq ( struct wled * wled ,
struct platform_device * pdev )
{
int rc ;
if ( ! wled - > has_short_detect )
return 0 ;
rc = regmap_update_bits ( wled - > regmap , wled - > ctrl_addr +
WLED4_CTRL_REG_SHORT_PROTECT ,
WLED4_CTRL_REG_SHORT_EN_MASK ,
WLED4_CTRL_REG_SHORT_EN_MASK ) ;
if ( rc < 0 )
return rc ;
wled - > short_irq = platform_get_irq_byname ( pdev , " short " ) ;
if ( wled - > short_irq < 0 ) {
dev_dbg ( & pdev - > dev , " short irq is not used \n " ) ;
return 0 ;
}
rc = devm_request_threaded_irq ( wled - > dev , wled - > short_irq ,
NULL , wled_short_irq_handler ,
IRQF_ONESHOT ,
" wled_short_irq " , wled ) ;
if ( rc < 0 )
dev_err ( wled - > dev , " Unable to request short_irq (err:%d) \n " ,
rc ) ;
return rc ;
}
2019-11-01 09:27:04 +03:00
static int wled_configure_ovp_irq ( struct wled * wled ,
struct platform_device * pdev )
{
int rc ;
u32 val ;
wled - > ovp_irq = platform_get_irq_byname ( pdev , " ovp " ) ;
if ( wled - > ovp_irq < 0 ) {
dev_dbg ( & pdev - > dev , " OVP IRQ not found - disabling automatic string detection \n " ) ;
return 0 ;
}
rc = devm_request_threaded_irq ( wled - > dev , wled - > ovp_irq , NULL ,
wled_ovp_irq_handler , IRQF_ONESHOT ,
" wled_ovp_irq " , wled ) ;
if ( rc < 0 ) {
dev_err ( wled - > dev , " Unable to request ovp_irq (err:%d) \n " ,
rc ) ;
wled - > ovp_irq = 0 ;
return 0 ;
}
rc = regmap_read ( wled - > regmap , wled - > ctrl_addr +
WLED3_CTRL_REG_MOD_EN , & val ) ;
if ( rc < 0 )
return rc ;
/* Keep OVP irq disabled until module is enabled */
if ( ! ( val & WLED3_CTRL_REG_MOD_EN_MASK ) )
disable_irq ( wled - > ovp_irq ) ;
return 0 ;
}
2019-11-01 09:27:00 +03:00
static const struct backlight_ops wled_ops = {
. update_status = wled_update_status ,
2015-07-22 03:44:49 +03:00
} ;
2019-11-01 09:27:00 +03:00
static int wled_probe ( struct platform_device * pdev )
2015-03-12 18:47:08 +03:00
{
2015-07-22 03:44:49 +03:00
struct backlight_properties props ;
struct backlight_device * bl ;
2019-11-01 09:27:00 +03:00
struct wled * wled ;
2015-03-12 18:47:08 +03:00
struct regmap * regmap ;
2015-10-26 20:45:08 +03:00
u32 val ;
2015-03-12 18:47:08 +03:00
int rc ;
regmap = dev_get_regmap ( pdev - > dev . parent , NULL ) ;
if ( ! regmap ) {
dev_err ( & pdev - > dev , " Unable to get regmap \n " ) ;
return - EINVAL ;
}
wled = devm_kzalloc ( & pdev - > dev , sizeof ( * wled ) , GFP_KERNEL ) ;
if ( ! wled )
return - ENOMEM ;
wled - > regmap = regmap ;
2019-11-01 09:27:01 +03:00
wled - > dev = & pdev - > dev ;
2015-03-12 18:47:08 +03:00
2020-04-23 18:33:35 +03:00
wled - > version = ( uintptr_t ) of_device_get_match_data ( & pdev - > dev ) ;
if ( ! wled - > version ) {
2019-11-01 09:27:01 +03:00
dev_err ( & pdev - > dev , " Unknown device version \n " ) ;
return - ENODEV ;
}
2015-03-12 18:47:08 +03:00
2019-11-01 09:27:03 +03:00
mutex_init ( & wled - > lock ) ;
2020-04-23 18:33:35 +03:00
rc = wled_configure ( wled ) ;
2015-03-12 18:47:08 +03:00
if ( rc )
return rc ;
2020-04-23 18:33:37 +03:00
val = WLED3_SINK_REG_BRIGHT_MAX ;
of_property_read_u32 ( pdev - > dev . of_node , " max-brightness " , & val ) ;
wled - > max_brightness = val ;
2020-04-23 18:33:35 +03:00
switch ( wled - > version ) {
2019-11-01 09:27:01 +03:00
case 3 :
2019-11-01 09:27:04 +03:00
wled - > cfg . auto_detection_enabled = false ;
2019-11-01 09:27:01 +03:00
rc = wled3_setup ( wled ) ;
if ( rc ) {
dev_err ( & pdev - > dev , " wled3_setup failed \n " ) ;
return rc ;
}
break ;
2019-11-01 09:27:02 +03:00
case 4 :
2019-11-01 09:27:03 +03:00
wled - > has_short_detect = true ;
2019-11-01 09:27:02 +03:00
rc = wled4_setup ( wled ) ;
if ( rc ) {
dev_err ( & pdev - > dev , " wled4_setup failed \n " ) ;
return rc ;
}
break ;
2020-04-23 18:33:37 +03:00
case 5 :
wled - > has_short_detect = true ;
if ( wled - > cfg . cabc_sel )
wled - > max_brightness = WLED5_SINK_REG_BRIGHT_MAX_12B ;
rc = wled5_setup ( wled ) ;
if ( rc ) {
dev_err ( & pdev - > dev , " wled5_setup failed \n " ) ;
return rc ;
}
break ;
2019-11-01 09:27:01 +03:00
default :
dev_err ( wled - > dev , " Invalid WLED version \n " ) ;
break ;
}
2019-11-01 09:27:04 +03:00
INIT_DELAYED_WORK ( & wled - > ovp_work , wled_ovp_work ) ;
2019-11-01 09:27:03 +03:00
rc = wled_configure_short_irq ( wled , pdev ) ;
if ( rc < 0 )
return rc ;
2019-11-01 09:27:04 +03:00
rc = wled_configure_ovp_irq ( wled , pdev ) ;
if ( rc < 0 )
return rc ;
2019-11-01 09:27:00 +03:00
val = WLED_DEFAULT_BRIGHTNESS ;
2015-10-26 20:45:08 +03:00
of_property_read_u32 ( pdev - > dev . of_node , " default-brightness " , & val ) ;
2015-07-22 03:44:49 +03:00
memset ( & props , 0 , sizeof ( struct backlight_properties ) ) ;
props . type = BACKLIGHT_RAW ;
2015-10-26 20:45:08 +03:00
props . brightness = val ;
2020-04-23 18:33:37 +03:00
props . max_brightness = wled - > max_brightness ;
2015-07-22 03:44:49 +03:00
bl = devm_backlight_device_register ( & pdev - > dev , wled - > name ,
& pdev - > dev , wled ,
2019-11-01 09:27:00 +03:00
& wled_ops , & props ) ;
2015-10-26 21:26:11 +03:00
return PTR_ERR_OR_ZERO ( bl ) ;
2015-03-12 18:47:08 +03:00
} ;
2019-11-01 09:27:03 +03:00
static int wled_remove ( struct platform_device * pdev )
{
2021-02-10 00:13:25 +03:00
struct wled * wled = platform_get_drvdata ( pdev ) ;
2019-11-01 09:27:03 +03:00
mutex_destroy ( & wled - > lock ) ;
2019-11-01 09:27:04 +03:00
cancel_delayed_work_sync ( & wled - > ovp_work ) ;
2019-11-01 09:27:03 +03:00
disable_irq ( wled - > short_irq ) ;
2019-11-01 09:27:04 +03:00
disable_irq ( wled - > ovp_irq ) ;
2019-11-01 09:27:03 +03:00
return 0 ;
}
2019-11-01 09:27:00 +03:00
static const struct of_device_id wled_match_table [ ] = {
2019-11-01 09:27:01 +03:00
{ . compatible = " qcom,pm8941-wled " , . data = ( void * ) 3 } ,
2021-02-28 15:41:05 +03:00
{ . compatible = " qcom,pmi8994-wled " , . data = ( void * ) 4 } ,
2019-11-01 09:27:02 +03:00
{ . compatible = " qcom,pmi8998-wled " , . data = ( void * ) 4 } ,
{ . compatible = " qcom,pm660l-wled " , . data = ( void * ) 4 } ,
2021-12-29 20:03:56 +03:00
{ . compatible = " qcom,pm6150l-wled " , . data = ( void * ) 5 } ,
2020-04-23 18:33:37 +03:00
{ . compatible = " qcom,pm8150l-wled " , . data = ( void * ) 5 } ,
2015-03-12 18:47:08 +03:00
{ }
} ;
2019-11-01 09:27:00 +03:00
MODULE_DEVICE_TABLE ( of , wled_match_table ) ;
2015-03-12 18:47:08 +03:00
2019-11-01 09:27:00 +03:00
static struct platform_driver wled_driver = {
. probe = wled_probe ,
2019-11-01 09:27:03 +03:00
. remove = wled_remove ,
2015-03-12 18:47:08 +03:00
. driver = {
2019-11-01 09:27:00 +03:00
. name = " qcom,wled " ,
. of_match_table = wled_match_table ,
2015-03-12 18:47:08 +03:00
} ,
} ;
2019-11-01 09:27:00 +03:00
module_platform_driver ( wled_driver ) ;
2015-03-12 18:47:08 +03:00
2019-11-01 09:27:00 +03:00
MODULE_DESCRIPTION ( " Qualcomm WLED driver " ) ;
2015-03-12 18:47:08 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;