diff mbox

[V4,5/8] backlight: qcom-wled: Restructure the driver for WLED3

Message ID 1531131741-19971-6-git-send-email-kgunda@codeaurora.org (mailing list archive)
State Not Applicable, archived
Delegated to: Andy Gross
Headers show

Commit Message

Kiran Gunda July 9, 2018, 10:22 a.m. UTC
Restructure the driver to add the support for new WLED
peripherals.

Signed-off-by: Kiran Gunda <kgunda@codeaurora.org>
---
Changes from V3:
	- This is the new patch after splitting the 
	  "backlight: qcom-wled: Add support for WLED4 peripheral" patch
	  to seperate the WLED3 specific restructure.
		
 drivers/video/backlight/qcom-wled.c | 396 ++++++++++++++++++++++--------------
 1 file changed, 246 insertions(+), 150 deletions(-)

Comments

Daniel Thompson July 20, 2018, 1:40 p.m. UTC | #1
On 09/07/18 11:22, Kiran Gunda wrote:
> Restructure the driver to add the support for new WLED
> peripherals.
> 
> Signed-off-by: Kiran Gunda <kgunda@codeaurora.org>

Acked-by: Daniel Thompson <daniel.thompson@linaro.org>


> ---
> Changes from V3:
> 	- This is the new patch after splitting the
> 	  "backlight: qcom-wled: Add support for WLED4 peripheral" patch
> 	  to seperate the WLED3 specific restructure.
> 		
>   drivers/video/backlight/qcom-wled.c | 396 ++++++++++++++++++++++--------------
>   1 file changed, 246 insertions(+), 150 deletions(-)
> 
> diff --git a/drivers/video/backlight/qcom-wled.c b/drivers/video/backlight/qcom-wled.c
> index 3cd6e75..87fc1d0 100644
> --- a/drivers/video/backlight/qcom-wled.c
> +++ b/drivers/video/backlight/qcom-wled.c
> @@ -15,59 +15,71 @@
>   #include <linux/module.h>
>   #include <linux/of.h>
>   #include <linux/of_device.h>
> +#include <linux/of_address.h>
>   #include <linux/regmap.h>
>   
>   /* From DT binding */
> +#define WLED_MAX_STRINGS				4
> +
>   #define WLED_DEFAULT_BRIGHTNESS				2048
>   
> -#define WLED3_SINK_REG_BRIGHT_MAX			0xFFF
> -#define WLED3_CTRL_REG_VAL_BASE				0x40
> +#define WLED_SINK_REG_BRIGHT_MAX			0xFFF
>   
> -/* WLED3 control registers */
> -#define WLED3_CTRL_REG_MOD_EN				0x46
> -#define  WLED3_CTRL_REG_MOD_EN_BIT			BIT(7)
> -#define  WLED3_CTRL_REG_MOD_EN_MASK			BIT(7)
> +/* WLED control registers */
> +#define WLED_CTRL_REG_MOD_EN				0x46
> +#define  WLED_CTRL_REG_MOD_EN_MASK			BIT(7)
> +#define  WLED_CTRL_REG_MOD_EN_SHIFT			7
>   
> -#define WLED3_CTRL_REG_FREQ				0x4c
> -#define  WLED3_CTRL_REG_FREQ_MASK			0x0f
> +#define WLED_CTRL_REG_FREQ				0x4c
> +#define  WLED_CTRL_REG_FREQ_MASK			GENMASK(3, 0)
>   
> -#define WLED3_CTRL_REG_OVP				0x4d
> -#define  WLED3_CTRL_REG_OVP_MASK			0x03
> +#define WLED_CTRL_REG_OVP				0x4d
> +#define  WLED_CTRL_REG_OVP_MASK				GENMASK(1, 0)
>   
> -#define WLED3_CTRL_REG_ILIMIT				0x4e
> -#define  WLED3_CTRL_REG_ILIMIT_MASK			0x07
> +#define WLED_CTRL_REG_ILIMIT				0x4e
> +#define  WLED_CTRL_REG_ILIMIT_MASK			GENMASK(2, 0)
>   
> -/* WLED3 sink registers */
> -#define WLED3_SINK_REG_SYNC				0x47
> -#define  WLED3_SINK_REG_SYNC_MASK			0x07
> -#define  WLED3_SINK_REG_SYNC_LED1			BIT(0)
> -#define  WLED3_SINK_REG_SYNC_LED2			BIT(1)
> -#define  WLED3_SINK_REG_SYNC_LED3			BIT(2)
> -#define  WLED3_SINK_REG_SYNC_ALL			0x07
> -#define  WLED3_SINK_REG_SYNC_CLEAR			0x00
> +/* WLED sink registers */
> +#define WLED_SINK_REG_SYNC				0x47
> +#define  WLED_SINK_REG_SYNC_CLEAR			0x00
>   
>   #define WLED3_SINK_REG_CURR_SINK			0x4f
> -#define  WLED3_SINK_REG_CURR_SINK_MASK			0xe0
> -#define  WLED3_SINK_REG_CURR_SINK_SHFT			0x05
> +#define  WLED3_SINK_REG_CURR_SINK_MASK			GENMASK(7, 5)
> +#define  WLED3_SINK_REG_CURR_SINK_SHFT			5
>   
> -/* WLED3 per-'string' registers below */
> -#define WLED3_SINK_REG_STR_OFFSET			0x10
> +/* WLED3 specific per-'string' registers below */
> +#define WLED3_SINK_REG_BRIGHT(n)			(0x40 + n)
>   
> -#define WLED3_SINK_REG_STR_MOD_EN_BASE			0x60
> +#define WLED3_SINK_REG_STR_MOD_EN(n)			(0x60 + (n * 0x10))
>   #define  WLED3_SINK_REG_STR_MOD_MASK			BIT(7)
> -#define  WLED3_SINK_REG_STR_MOD_EN			BIT(7)
>   
> -#define WLED3_SINK_REG_STR_FULL_SCALE_CURR		0x62
> -#define  WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK	0x1f
> +#define WLED3_SINK_REG_STR_FULL_SCALE_CURR(n)		(0x62 + (n * 0x10))
> +#define  WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK	GENMASK(4, 0)
>   
> -#define WLED3_SINK_REG_STR_MOD_SRC_BASE			0x63
> -#define  WLED3_SINK_REG_STR_MOD_SRC_MASK		0x01
> +#define WLED3_SINK_REG_STR_MOD_SRC(n)			(0x63 + (n * 0x10))
> +#define  WLED3_SINK_REG_STR_MOD_SRC_MASK		BIT(0)
>   #define  WLED3_SINK_REG_STR_MOD_SRC_INT			0x00
>   #define  WLED3_SINK_REG_STR_MOD_SRC_EXT			0x01
>   
> -#define WLED3_SINK_REG_STR_CABC_BASE			0x66
> +#define WLED3_SINK_REG_STR_CABC(n)			(0x66 + (n * 0x10))
>   #define  WLED3_SINK_REG_STR_CABC_MASK			BIT(7)
> -#define  WLED3_SINK_REG_STR_CABC_EN			BIT(7)
> +
> +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;
> +};
>   
>   struct wled_config {
>   	u32 boost_i_limit;
> @@ -75,132 +87,179 @@ struct wled_config {
>   	u32 switch_freq;
>   	u32 num_strings;
>   	u32 string_i_limit;
> +	u32 enabled_strings[WLED_MAX_STRINGS];
>   	bool cs_out_en;
>   	bool ext_gen;
> -	bool cabc_en;
> +	bool cabc;
>   };
>   
>   struct wled {
>   	const char *name;
> +	struct device *dev;
>   	struct regmap *regmap;
> -	u16 addr;
> +	u16 ctrl_addr;
> +	u16 max_string_count;
> +	u32 brightness;
> +	u32 max_brightness;
>   
>   	struct wled_config cfg;
> +	int (*wled_set_brightness)(struct wled *wled, u16 brightness);
>   };
>   
> +static int wled3_set_brightness(struct wled *wled, u16 brightness)
> +{
> +	int rc, i;
> +	u8 v[2];
> +
> +	v[0] = brightness & 0xff;
> +	v[1] = (brightness >> 8) & 0xf;
> +
> +	for (i = 0;  i < wled->cfg.num_strings; ++i) {
> +		rc = regmap_bulk_write(wled->regmap, wled->ctrl_addr +
> +				       WLED3_SINK_REG_BRIGHT(i), v, 2);
> +		if (rc < 0)
> +			return rc;
> +	}
> +
> +	return 0;
> +}
> +
> +static int wled_module_enable(struct wled *wled, int val)
> +{
> +	int rc;
> +
> +	rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
> +				WLED_CTRL_REG_MOD_EN,
> +				WLED_CTRL_REG_MOD_EN_MASK,
> +				val << WLED_CTRL_REG_MOD_EN_SHIFT);
> +	return rc;
> +}
> +
> +static int wled_sync_toggle(struct wled *wled)
> +{
> +	int rc;
> +	unsigned int mask = GENMASK(wled->max_string_count - 1, 0);
> +
> +	rc = regmap_update_bits(wled->regmap,
> +				wled->ctrl_addr + WLED_SINK_REG_SYNC,
> +				mask, mask);
> +	if (rc < 0)
> +		return rc;
> +
> +	rc = regmap_update_bits(wled->regmap,
> +				wled->ctrl_addr + WLED_SINK_REG_SYNC,
> +				mask, WLED_SINK_REG_SYNC_CLEAR);
> +
> +	return rc;
> +}
> +
>   static int wled_update_status(struct backlight_device *bl)
>   {
>   	struct wled *wled = bl_get_data(bl);
> -	u16 val = bl->props.brightness;
> -	u8 ctrl = 0;
> -	int rc;
> -	int i;
> +	u16 brightness = bl->props.brightness;
> +	int rc = 0;
>   
>   	if (bl->props.power != FB_BLANK_UNBLANK ||
>   	    bl->props.fb_blank != FB_BLANK_UNBLANK ||
>   	    bl->props.state & BL_CORE_FBBLANK)
> -		val = 0;
> -
> -	if (val != 0)
> -		ctrl = WLED3_CTRL_REG_MOD_EN_BIT;
> +		brightness = 0;
>   
> -	rc = regmap_update_bits(wled->regmap,
> -			wled->addr + WLED3_CTRL_REG_MOD_EN,
> -			WLED3_CTRL_REG_MOD_EN_MASK, ctrl);
> -	if (rc)
> -		return rc;
> +	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);
> +			return rc;
> +		}
>   
> -	for (i = 0; i < wled->cfg.num_strings; ++i) {
> -		u8 v[2] = { val & 0xff, (val >> 8) & 0xf };
> +		rc = wled_sync_toggle(wled);
> +		if (rc < 0) {
> +			dev_err(wled->dev, "wled sync failed rc:%d\n", rc);
> +			return rc;
> +		}
> +	}
>   
> -		rc = regmap_bulk_write(wled->regmap,
> -				wled->addr + WLED3_CTRL_REG_VAL_BASE + 2 * i,
> -				v, 2);
> -		if (rc)
> +	if (!!brightness != !!wled->brightness) {
> +		rc = wled_module_enable(wled, !!brightness);
> +		if (rc < 0) {
> +			dev_err(wled->dev, "wled enable failed rc:%d\n", rc);
>   			return rc;
> +		}
>   	}
>   
> -	rc = regmap_update_bits(wled->regmap,
> -			wled->addr + WLED3_SINK_REG_SYNC,
> -			WLED3_SINK_REG_SYNC_MASK, WLED3_SINK_REG_SYNC_ALL);
> -	if (rc)
> -		return rc;
> +	wled->brightness = brightness;
>   
> -	rc = regmap_update_bits(wled->regmap,
> -			wled->addr + WLED3_SINK_REG_SYNC,
> -			WLED3_SINK_REG_SYNC_MASK, WLED3_SINK_REG_SYNC_CLEAR);
>   	return rc;
>   }
>   
> -static int wled_setup(struct wled *wled)
> +static int wled3_setup(struct wled *wled)
>   {
> -	int rc;
> -	int i;
> +	u16 addr;
> +	u8 sink_en = 0;
> +	int rc, i, j;
>   
>   	rc = regmap_update_bits(wled->regmap,
> -			wled->addr + WLED3_CTRL_REG_OVP,
> -			WLED3_CTRL_REG_OVP_MASK, wled->cfg.ovp);
> +				wled->ctrl_addr + WLED_CTRL_REG_OVP,
> +				WLED_CTRL_REG_OVP_MASK, wled->cfg.ovp);
>   	if (rc)
>   		return rc;
>   
>   	rc = regmap_update_bits(wled->regmap,
> -			wled->addr + WLED3_CTRL_REG_ILIMIT,
> -			WLED3_CTRL_REG_ILIMIT_MASK, wled->cfg.boost_i_limit);
> +				wled->ctrl_addr + WLED_CTRL_REG_ILIMIT,
> +				WLED_CTRL_REG_ILIMIT_MASK,
> +				wled->cfg.boost_i_limit);
>   	if (rc)
>   		return rc;
>   
>   	rc = regmap_update_bits(wled->regmap,
> -			wled->addr + WLED3_CTRL_REG_FREQ,
> -			WLED3_CTRL_REG_FREQ_MASK, wled->cfg.switch_freq);
> +				wled->ctrl_addr + WLED_CTRL_REG_FREQ,
> +				WLED_CTRL_REG_FREQ_MASK,
> +				wled->cfg.switch_freq);
>   	if (rc)
>   		return rc;
>   
> -	if (wled->cfg.cs_out_en) {
> -		u8 all = (BIT(wled->cfg.num_strings) - 1)
> -				<< WLED3_SINK_REG_CURR_SINK_SHFT;
> -
> -		rc = regmap_update_bits(wled->regmap,
> -				wled->addr + WLED3_SINK_REG_CURR_SINK,
> -				WLED3_SINK_REG_CURR_SINK_MASK, all);
> -		if (rc)
> -			return rc;
> -	}
> -
>   	for (i = 0; i < wled->cfg.num_strings; ++i) {
> -		u16 addr = wled->addr + WLED3_SINK_REG_STR_OFFSET * i;
> -
> -		rc = regmap_update_bits(wled->regmap,
> -				addr + WLED3_SINK_REG_STR_MOD_EN_BASE,
> -				WLED3_SINK_REG_STR_MOD_MASK,
> -				WLED3_SINK_REG_STR_MOD_EN);
> +		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);
>   		if (rc)
>   			return rc;
>   
>   		if (wled->cfg.ext_gen) {
> -			rc = regmap_update_bits(wled->regmap,
> -					addr + WLED3_SINK_REG_STR_MOD_SRC_BASE,
> -					WLED3_SINK_REG_STR_MOD_SRC_MASK,
> -					WLED3_SINK_REG_STR_MOD_SRC_EXT);
> +			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);
>   			if (rc)
>   				return rc;
>   		}
>   
> -		rc = regmap_update_bits(wled->regmap,
> -				addr + WLED3_SINK_REG_STR_FULL_SCALE_CURR,
> -				WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK,
> -				wled->cfg.string_i_limit);
> +		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);
>   		if (rc)
>   			return rc;
>   
> -		rc = regmap_update_bits(wled->regmap,
> -				addr + WLED3_SINK_REG_STR_CABC_BASE,
> -				WLED3_SINK_REG_STR_CABC_MASK,
> -				wled->cfg.cabc_en ?
> -					WLED3_SINK_REG_STR_CABC_EN : 0);
> +		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);
>   		if (rc)
>   			return rc;
> +
> +		sink_en |= BIT(j + WLED3_SINK_REG_CURR_SINK_SHFT);
>   	}
>   
> +	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;
> +
>   	return 0;
>   }
>   
> @@ -208,17 +267,11 @@ static int wled_setup(struct wled *wled)
>   	.boost_i_limit = 3,
>   	.string_i_limit = 20,
>   	.ovp = 2,
> +	.num_strings = 3,
>   	.switch_freq = 5,
> -	.num_strings = 0,
>   	.cs_out_en = false,
>   	.ext_gen = false,
> -	.cabc_en = false,
> -};
> -
> -struct wled_var_cfg {
> -	const u32 *values;
> -	u32 (*fn)(u32);
> -	int size;
> +	.cabc = false,
>   };
>   
>   static const u32 wled3_boost_i_limit_values[] = {
> @@ -263,7 +316,11 @@ static u32 wled3_switch_freq_values_fn(u32 idx)
>   	.size = 26,
>   };
>   
> -static u32 wled3_values(const struct wled_var_cfg *cfg, u32 idx)
> +static const struct wled_var_cfg wled3_string_cfg = {
> +	.size = 8,
> +};
> +
> +static u32 wled_values(const struct wled_var_cfg *cfg, u32 idx)
>   {
>   	if (idx >= cfg->size)
>   		return UINT_MAX;
> @@ -274,68 +331,77 @@ static u32 wled3_values(const struct wled_var_cfg *cfg, u32 idx)
>   	return idx;
>   }
>   
> -static int wled_configure(struct wled *wled, struct device *dev)
> +#define	WLED3	3
> +
> +static int wled_configure(struct wled *wled, int version)
>   {
>   	struct wled_config *cfg = &wled->cfg;
> -	u32 val;
> -	int rc;
> -	u32 c;
> -	int i;
> -	int j;
> -
> -	const struct {
> -		const char *name;
> -		u32 *val_ptr;
> -		const struct wled_var_cfg *cfg;
> -	} u32_opts[] = {
> +	struct device *dev = wled->dev;
> +	const __be32 *prop_addr;
> +	u32 size, val, c, string_len;
> +	int rc, i, j;
> +
> +	const struct wled_u32_opts *u32_opts = NULL;
> +	const struct wled_u32_opts wled3_opts[] = {
>   		{
> -			"qcom,current-boost-limit",
> -			&cfg->boost_i_limit,
> +			.name = "qcom,current-boost-limit",
> +			.val_ptr = &cfg->boost_i_limit,
>   			.cfg = &wled3_boost_i_limit_cfg,
>   		},
>   		{
> -			"qcom,current-limit",
> -			&cfg->string_i_limit,
> +			.name = "qcom,current-limit",
> +			.val_ptr = &cfg->string_i_limit,
>   			.cfg = &wled3_string_i_limit_cfg,
>   		},
>   		{
> -			"qcom,ovp",
> -			&cfg->ovp,
> +			.name = "qcom,ovp",
> +			.val_ptr = &cfg->ovp,
>   			.cfg = &wled3_ovp_cfg,
>   		},
>   		{
> -			"qcom,switching-freq",
> -			&cfg->switch_freq,
> +			.name = "qcom,switching-freq",
> +			.val_ptr = &cfg->switch_freq,
>   			.cfg = &wled3_switch_freq_cfg,
>   		},
>   		{
> -			"qcom,num-strings",
> -			&cfg->num_strings,
> +			.name = "qcom,num-strings",
> +			.val_ptr = &cfg->num_strings,
>   			.cfg = &wled3_num_strings_cfg,
>   		},
>   	};
> -	const struct {
> -		const char *name;
> -		bool *val_ptr;
> -	} bool_opts[] = {
> +
> +	const struct wled_bool_opts bool_opts[] = {
>   		{ "qcom,cs-out", &cfg->cs_out_en, },
>   		{ "qcom,ext-gen", &cfg->ext_gen, },
> -		{ "qcom,cabc", &cfg->cabc_en, },
> +		{ "qcom,cabc", &cfg->cabc, },
>   	};
>   
> -	rc = of_property_read_u32(dev->of_node, "reg", &val);
> -	if (rc || val > 0xffff) {
> -		dev_err(dev, "invalid IO resources\n");
> -		return rc ? rc : -EINVAL;
> +	prop_addr = of_get_address(dev->of_node, 0, NULL, NULL);
> +	if (!prop_addr) {
> +		dev_err(wled->dev, "invalid IO resources\n");
> +		return -EINVAL;
>   	}
> -	wled->addr = val;
> +	wled->ctrl_addr = be32_to_cpu(*prop_addr);
>   
>   	rc = of_property_read_string(dev->of_node, "label", &wled->name);
>   	if (rc)
>   		wled->name = dev->of_node->name;
>   
> -	*cfg = wled3_config_defaults;
> -	for (i = 0; i < ARRAY_SIZE(u32_opts); ++i) {
> +	switch (version) {
> +	case WLED3:
> +		u32_opts = wled3_opts;
> +		size = ARRAY_SIZE(wled3_opts);
> +		*cfg = wled3_config_defaults;
> +		wled->wled_set_brightness = wled3_set_brightness;
> +		wled->max_string_count = 3;
> +		break;
> +
> +	default:
> +		dev_err(wled->dev, "Invalid WLED version\n");
> +		break;
> +	}
> +
> +	for (i = 0; i < size; ++i) {
>   		rc = of_property_read_u32(dev->of_node, u32_opts[i].name, &val);
>   		if (rc == -EINVAL) {
>   			continue;
> @@ -346,12 +412,15 @@ static int wled_configure(struct wled *wled, struct device *dev)
>   
>   		c = UINT_MAX;
>   		for (j = 0; c != val; j++) {
> -			c = wled3_values(u32_opts[i].cfg, j);
> +			c = wled_values(u32_opts[i].cfg, j);
>   			if (c == UINT_MAX) {
>   				dev_err(dev, "invalid value for '%s'\n",
>   					u32_opts[i].name);
>   				return -EINVAL;
>   			}
> +
> +			if (c == val)
> +				break;
>   		}
>   
>   		dev_dbg(dev, "'%s' = %u\n", u32_opts[i].name, c);
> @@ -365,6 +434,15 @@ static int wled_configure(struct wled *wled, struct device *dev)
>   
>   	cfg->num_strings = cfg->num_strings + 1;
>   
> +	string_len = of_property_count_elems_of_size(dev->of_node,
> +						     "qcom,enabled-strings",
> +						     sizeof(u32));
> +	if (string_len > 0)
> +		rc = of_property_read_u32_array(dev->of_node,
> +						"qcom,enabled-strings",
> +						wled->cfg.enabled_strings,
> +						sizeof(u32));
> +
>   	return 0;
>   }
>   
> @@ -378,6 +456,7 @@ static int wled_probe(struct platform_device *pdev)
>   	struct backlight_device *bl;
>   	struct wled *wled;
>   	struct regmap *regmap;
> +	int version;
>   	u32 val;
>   	int rc;
>   
> @@ -392,22 +471,39 @@ static int wled_probe(struct platform_device *pdev)
>   		return -ENOMEM;
>   
>   	wled->regmap = regmap;
> +	wled->dev = &pdev->dev;
>   
> -	rc = wled_configure(wled, &pdev->dev);
> -	if (rc)
> -		return rc;
> +	version = (uintptr_t)of_device_get_match_data(&pdev->dev);
> +	if (!version) {
> +		dev_err(&pdev->dev, "Unknown device version\n");
> +		return -ENODEV;
> +	}
>   
> -	rc = wled_setup(wled);
> +	rc = wled_configure(wled, version);
>   	if (rc)
>   		return rc;
>   
> +	switch (version) {
> +	case WLED3:
> +		rc = wled3_setup(wled);
> +		if (rc) {
> +			dev_err(&pdev->dev, "wled3_setup failed\n");
> +			return rc;
> +		}
> +		break;
> +
> +	default:
> +		dev_err(wled->dev, "Invalid WLED version\n");
> +		break;
> +	}
> +
>   	val = WLED_DEFAULT_BRIGHTNESS;
>   	of_property_read_u32(pdev->dev.of_node, "default-brightness", &val);
>   
>   	memset(&props, 0, sizeof(struct backlight_properties));
>   	props.type = BACKLIGHT_RAW;
>   	props.brightness = val;
> -	props.max_brightness = WLED3_SINK_REG_BRIGHT_MAX;
> +	props.max_brightness = WLED_SINK_REG_BRIGHT_MAX;
>   	bl = devm_backlight_device_register(&pdev->dev, wled->name,
>   					    &pdev->dev, wled,
>   					    &wled_ops, &props);
> @@ -415,7 +511,7 @@ static int wled_probe(struct platform_device *pdev)
>   };
>   
>   static const struct of_device_id wled_match_table[] = {
> -	{ .compatible = "qcom,pm8941-wled" },
> +	{ .compatible = "qcom,pm8941-wled", .data = (void *)3 },
>   	{}
>   };
>   MODULE_DEVICE_TABLE(of, wled_match_table);
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Bjorn Andersson Aug. 7, 2018, 5:37 a.m. UTC | #2
On Mon 09 Jul 03:22 PDT 2018, Kiran Gunda wrote:
> diff --git a/drivers/video/backlight/qcom-wled.c b/drivers/video/backlight/qcom-wled.c
[..]
> @@ -365,6 +434,15 @@ static int wled_configure(struct wled *wled, struct device *dev)
>  
>  	cfg->num_strings = cfg->num_strings + 1;
>  
> +	string_len = of_property_count_elems_of_size(dev->of_node,
> +						     "qcom,enabled-strings",
> +						     sizeof(u32));
> +	if (string_len > 0)
> +		rc = of_property_read_u32_array(dev->of_node,
> +						"qcom,enabled-strings",
> +						wled->cfg.enabled_strings,

qcom,enabled-strings is listed as optional, but without it we will end
up with qcom,num-strings zeros in an array.  Initialize
wled->cfg.enabled_strings to 0,1,2,3 and the driver will be backwards
compatible.

I also think that if you do qcom,enabled-strings = <0, 1, 2>; there's no
need to also specify qcom,num-strings = <3>; and we can just use
string_len for num_strings.

> +						sizeof(u32));
> +
>  	return 0;
>  }

Regards,
Bjorn
--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Kiran Gunda Aug. 16, 2018, 5:28 a.m. UTC | #3
On 2018-08-07 11:07, Bjorn Andersson wrote:
> On Mon 09 Jul 03:22 PDT 2018, Kiran Gunda wrote:
>> diff --git a/drivers/video/backlight/qcom-wled.c 
>> b/drivers/video/backlight/qcom-wled.c
> [..]
>> @@ -365,6 +434,15 @@ static int wled_configure(struct wled *wled, 
>> struct device *dev)
>> 
>>  	cfg->num_strings = cfg->num_strings + 1;
>> 
>> +	string_len = of_property_count_elems_of_size(dev->of_node,
>> +						     "qcom,enabled-strings",
>> +						     sizeof(u32));
>> +	if (string_len > 0)
>> +		rc = of_property_read_u32_array(dev->of_node,
>> +						"qcom,enabled-strings",
>> +						wled->cfg.enabled_strings,
> 
> qcom,enabled-strings is listed as optional, but without it we will end
> up with qcom,num-strings zeros in an array.  Initialize
> wled->cfg.enabled_strings to 0,1,2,3 and the driver will be backwards
> compatible.
> 
Correct. I will initialize the array in the next series.
> I also think that if you do qcom,enabled-strings = <0, 1, 2>; there's 
> no
> need to also specify qcom,num-strings = <3>; and we can just use
> string_len for num_strings.
Correct. But I have kept this property to have the backward 
compatibility with the
old driver (WLED3).
> 
>> +						sizeof(u32));
>> +
>>  	return 0;
>>  }
> 
> Regards,
> Bjorn
> --
> To unsubscribe from this list: send the line "unsubscribe 
> linux-arm-msm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/video/backlight/qcom-wled.c b/drivers/video/backlight/qcom-wled.c
index 3cd6e75..87fc1d0 100644
--- a/drivers/video/backlight/qcom-wled.c
+++ b/drivers/video/backlight/qcom-wled.c
@@ -15,59 +15,71 @@ 
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
+#include <linux/of_address.h>
 #include <linux/regmap.h>
 
 /* From DT binding */
+#define WLED_MAX_STRINGS				4
+
 #define WLED_DEFAULT_BRIGHTNESS				2048
 
-#define WLED3_SINK_REG_BRIGHT_MAX			0xFFF
-#define WLED3_CTRL_REG_VAL_BASE				0x40
+#define WLED_SINK_REG_BRIGHT_MAX			0xFFF
 
-/* WLED3 control registers */
-#define WLED3_CTRL_REG_MOD_EN				0x46
-#define  WLED3_CTRL_REG_MOD_EN_BIT			BIT(7)
-#define  WLED3_CTRL_REG_MOD_EN_MASK			BIT(7)
+/* WLED control registers */
+#define WLED_CTRL_REG_MOD_EN				0x46
+#define  WLED_CTRL_REG_MOD_EN_MASK			BIT(7)
+#define  WLED_CTRL_REG_MOD_EN_SHIFT			7
 
-#define WLED3_CTRL_REG_FREQ				0x4c
-#define  WLED3_CTRL_REG_FREQ_MASK			0x0f
+#define WLED_CTRL_REG_FREQ				0x4c
+#define  WLED_CTRL_REG_FREQ_MASK			GENMASK(3, 0)
 
-#define WLED3_CTRL_REG_OVP				0x4d
-#define  WLED3_CTRL_REG_OVP_MASK			0x03
+#define WLED_CTRL_REG_OVP				0x4d
+#define  WLED_CTRL_REG_OVP_MASK				GENMASK(1, 0)
 
-#define WLED3_CTRL_REG_ILIMIT				0x4e
-#define  WLED3_CTRL_REG_ILIMIT_MASK			0x07
+#define WLED_CTRL_REG_ILIMIT				0x4e
+#define  WLED_CTRL_REG_ILIMIT_MASK			GENMASK(2, 0)
 
-/* WLED3 sink registers */
-#define WLED3_SINK_REG_SYNC				0x47
-#define  WLED3_SINK_REG_SYNC_MASK			0x07
-#define  WLED3_SINK_REG_SYNC_LED1			BIT(0)
-#define  WLED3_SINK_REG_SYNC_LED2			BIT(1)
-#define  WLED3_SINK_REG_SYNC_LED3			BIT(2)
-#define  WLED3_SINK_REG_SYNC_ALL			0x07
-#define  WLED3_SINK_REG_SYNC_CLEAR			0x00
+/* WLED sink registers */
+#define WLED_SINK_REG_SYNC				0x47
+#define  WLED_SINK_REG_SYNC_CLEAR			0x00
 
 #define WLED3_SINK_REG_CURR_SINK			0x4f
-#define  WLED3_SINK_REG_CURR_SINK_MASK			0xe0
-#define  WLED3_SINK_REG_CURR_SINK_SHFT			0x05
+#define  WLED3_SINK_REG_CURR_SINK_MASK			GENMASK(7, 5)
+#define  WLED3_SINK_REG_CURR_SINK_SHFT			5
 
-/* WLED3 per-'string' registers below */
-#define WLED3_SINK_REG_STR_OFFSET			0x10
+/* WLED3 specific per-'string' registers below */
+#define WLED3_SINK_REG_BRIGHT(n)			(0x40 + n)
 
-#define WLED3_SINK_REG_STR_MOD_EN_BASE			0x60
+#define WLED3_SINK_REG_STR_MOD_EN(n)			(0x60 + (n * 0x10))
 #define  WLED3_SINK_REG_STR_MOD_MASK			BIT(7)
-#define  WLED3_SINK_REG_STR_MOD_EN			BIT(7)
 
-#define WLED3_SINK_REG_STR_FULL_SCALE_CURR		0x62
-#define  WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK	0x1f
+#define WLED3_SINK_REG_STR_FULL_SCALE_CURR(n)		(0x62 + (n * 0x10))
+#define  WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK	GENMASK(4, 0)
 
-#define WLED3_SINK_REG_STR_MOD_SRC_BASE			0x63
-#define  WLED3_SINK_REG_STR_MOD_SRC_MASK		0x01
+#define WLED3_SINK_REG_STR_MOD_SRC(n)			(0x63 + (n * 0x10))
+#define  WLED3_SINK_REG_STR_MOD_SRC_MASK		BIT(0)
 #define  WLED3_SINK_REG_STR_MOD_SRC_INT			0x00
 #define  WLED3_SINK_REG_STR_MOD_SRC_EXT			0x01
 
-#define WLED3_SINK_REG_STR_CABC_BASE			0x66
+#define WLED3_SINK_REG_STR_CABC(n)			(0x66 + (n * 0x10))
 #define  WLED3_SINK_REG_STR_CABC_MASK			BIT(7)
-#define  WLED3_SINK_REG_STR_CABC_EN			BIT(7)
+
+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;
+};
 
 struct wled_config {
 	u32 boost_i_limit;
@@ -75,132 +87,179 @@  struct wled_config {
 	u32 switch_freq;
 	u32 num_strings;
 	u32 string_i_limit;
+	u32 enabled_strings[WLED_MAX_STRINGS];
 	bool cs_out_en;
 	bool ext_gen;
-	bool cabc_en;
+	bool cabc;
 };
 
 struct wled {
 	const char *name;
+	struct device *dev;
 	struct regmap *regmap;
-	u16 addr;
+	u16 ctrl_addr;
+	u16 max_string_count;
+	u32 brightness;
+	u32 max_brightness;
 
 	struct wled_config cfg;
+	int (*wled_set_brightness)(struct wled *wled, u16 brightness);
 };
 
+static int wled3_set_brightness(struct wled *wled, u16 brightness)
+{
+	int rc, i;
+	u8 v[2];
+
+	v[0] = brightness & 0xff;
+	v[1] = (brightness >> 8) & 0xf;
+
+	for (i = 0;  i < wled->cfg.num_strings; ++i) {
+		rc = regmap_bulk_write(wled->regmap, wled->ctrl_addr +
+				       WLED3_SINK_REG_BRIGHT(i), v, 2);
+		if (rc < 0)
+			return rc;
+	}
+
+	return 0;
+}
+
+static int wled_module_enable(struct wled *wled, int val)
+{
+	int rc;
+
+	rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
+				WLED_CTRL_REG_MOD_EN,
+				WLED_CTRL_REG_MOD_EN_MASK,
+				val << WLED_CTRL_REG_MOD_EN_SHIFT);
+	return rc;
+}
+
+static int wled_sync_toggle(struct wled *wled)
+{
+	int rc;
+	unsigned int mask = GENMASK(wled->max_string_count - 1, 0);
+
+	rc = regmap_update_bits(wled->regmap,
+				wled->ctrl_addr + WLED_SINK_REG_SYNC,
+				mask, mask);
+	if (rc < 0)
+		return rc;
+
+	rc = regmap_update_bits(wled->regmap,
+				wled->ctrl_addr + WLED_SINK_REG_SYNC,
+				mask, WLED_SINK_REG_SYNC_CLEAR);
+
+	return rc;
+}
+
 static int wled_update_status(struct backlight_device *bl)
 {
 	struct wled *wled = bl_get_data(bl);
-	u16 val = bl->props.brightness;
-	u8 ctrl = 0;
-	int rc;
-	int i;
+	u16 brightness = bl->props.brightness;
+	int rc = 0;
 
 	if (bl->props.power != FB_BLANK_UNBLANK ||
 	    bl->props.fb_blank != FB_BLANK_UNBLANK ||
 	    bl->props.state & BL_CORE_FBBLANK)
-		val = 0;
-
-	if (val != 0)
-		ctrl = WLED3_CTRL_REG_MOD_EN_BIT;
+		brightness = 0;
 
-	rc = regmap_update_bits(wled->regmap,
-			wled->addr + WLED3_CTRL_REG_MOD_EN,
-			WLED3_CTRL_REG_MOD_EN_MASK, ctrl);
-	if (rc)
-		return rc;
+	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);
+			return rc;
+		}
 
-	for (i = 0; i < wled->cfg.num_strings; ++i) {
-		u8 v[2] = { val & 0xff, (val >> 8) & 0xf };
+		rc = wled_sync_toggle(wled);
+		if (rc < 0) {
+			dev_err(wled->dev, "wled sync failed rc:%d\n", rc);
+			return rc;
+		}
+	}
 
-		rc = regmap_bulk_write(wled->regmap,
-				wled->addr + WLED3_CTRL_REG_VAL_BASE + 2 * i,
-				v, 2);
-		if (rc)
+	if (!!brightness != !!wled->brightness) {
+		rc = wled_module_enable(wled, !!brightness);
+		if (rc < 0) {
+			dev_err(wled->dev, "wled enable failed rc:%d\n", rc);
 			return rc;
+		}
 	}
 
-	rc = regmap_update_bits(wled->regmap,
-			wled->addr + WLED3_SINK_REG_SYNC,
-			WLED3_SINK_REG_SYNC_MASK, WLED3_SINK_REG_SYNC_ALL);
-	if (rc)
-		return rc;
+	wled->brightness = brightness;
 
-	rc = regmap_update_bits(wled->regmap,
-			wled->addr + WLED3_SINK_REG_SYNC,
-			WLED3_SINK_REG_SYNC_MASK, WLED3_SINK_REG_SYNC_CLEAR);
 	return rc;
 }
 
-static int wled_setup(struct wled *wled)
+static int wled3_setup(struct wled *wled)
 {
-	int rc;
-	int i;
+	u16 addr;
+	u8 sink_en = 0;
+	int rc, i, j;
 
 	rc = regmap_update_bits(wled->regmap,
-			wled->addr + WLED3_CTRL_REG_OVP,
-			WLED3_CTRL_REG_OVP_MASK, wled->cfg.ovp);
+				wled->ctrl_addr + WLED_CTRL_REG_OVP,
+				WLED_CTRL_REG_OVP_MASK, wled->cfg.ovp);
 	if (rc)
 		return rc;
 
 	rc = regmap_update_bits(wled->regmap,
-			wled->addr + WLED3_CTRL_REG_ILIMIT,
-			WLED3_CTRL_REG_ILIMIT_MASK, wled->cfg.boost_i_limit);
+				wled->ctrl_addr + WLED_CTRL_REG_ILIMIT,
+				WLED_CTRL_REG_ILIMIT_MASK,
+				wled->cfg.boost_i_limit);
 	if (rc)
 		return rc;
 
 	rc = regmap_update_bits(wled->regmap,
-			wled->addr + WLED3_CTRL_REG_FREQ,
-			WLED3_CTRL_REG_FREQ_MASK, wled->cfg.switch_freq);
+				wled->ctrl_addr + WLED_CTRL_REG_FREQ,
+				WLED_CTRL_REG_FREQ_MASK,
+				wled->cfg.switch_freq);
 	if (rc)
 		return rc;
 
-	if (wled->cfg.cs_out_en) {
-		u8 all = (BIT(wled->cfg.num_strings) - 1)
-				<< WLED3_SINK_REG_CURR_SINK_SHFT;
-
-		rc = regmap_update_bits(wled->regmap,
-				wled->addr + WLED3_SINK_REG_CURR_SINK,
-				WLED3_SINK_REG_CURR_SINK_MASK, all);
-		if (rc)
-			return rc;
-	}
-
 	for (i = 0; i < wled->cfg.num_strings; ++i) {
-		u16 addr = wled->addr + WLED3_SINK_REG_STR_OFFSET * i;
-
-		rc = regmap_update_bits(wled->regmap,
-				addr + WLED3_SINK_REG_STR_MOD_EN_BASE,
-				WLED3_SINK_REG_STR_MOD_MASK,
-				WLED3_SINK_REG_STR_MOD_EN);
+		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);
 		if (rc)
 			return rc;
 
 		if (wled->cfg.ext_gen) {
-			rc = regmap_update_bits(wled->regmap,
-					addr + WLED3_SINK_REG_STR_MOD_SRC_BASE,
-					WLED3_SINK_REG_STR_MOD_SRC_MASK,
-					WLED3_SINK_REG_STR_MOD_SRC_EXT);
+			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);
 			if (rc)
 				return rc;
 		}
 
-		rc = regmap_update_bits(wled->regmap,
-				addr + WLED3_SINK_REG_STR_FULL_SCALE_CURR,
-				WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK,
-				wled->cfg.string_i_limit);
+		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);
 		if (rc)
 			return rc;
 
-		rc = regmap_update_bits(wled->regmap,
-				addr + WLED3_SINK_REG_STR_CABC_BASE,
-				WLED3_SINK_REG_STR_CABC_MASK,
-				wled->cfg.cabc_en ?
-					WLED3_SINK_REG_STR_CABC_EN : 0);
+		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);
 		if (rc)
 			return rc;
+
+		sink_en |= BIT(j + WLED3_SINK_REG_CURR_SINK_SHFT);
 	}
 
+	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;
+
 	return 0;
 }
 
@@ -208,17 +267,11 @@  static int wled_setup(struct wled *wled)
 	.boost_i_limit = 3,
 	.string_i_limit = 20,
 	.ovp = 2,
+	.num_strings = 3,
 	.switch_freq = 5,
-	.num_strings = 0,
 	.cs_out_en = false,
 	.ext_gen = false,
-	.cabc_en = false,
-};
-
-struct wled_var_cfg {
-	const u32 *values;
-	u32 (*fn)(u32);
-	int size;
+	.cabc = false,
 };
 
 static const u32 wled3_boost_i_limit_values[] = {
@@ -263,7 +316,11 @@  static u32 wled3_switch_freq_values_fn(u32 idx)
 	.size = 26,
 };
 
-static u32 wled3_values(const struct wled_var_cfg *cfg, u32 idx)
+static const struct wled_var_cfg wled3_string_cfg = {
+	.size = 8,
+};
+
+static u32 wled_values(const struct wled_var_cfg *cfg, u32 idx)
 {
 	if (idx >= cfg->size)
 		return UINT_MAX;
@@ -274,68 +331,77 @@  static u32 wled3_values(const struct wled_var_cfg *cfg, u32 idx)
 	return idx;
 }
 
-static int wled_configure(struct wled *wled, struct device *dev)
+#define	WLED3	3
+
+static int wled_configure(struct wled *wled, int version)
 {
 	struct wled_config *cfg = &wled->cfg;
-	u32 val;
-	int rc;
-	u32 c;
-	int i;
-	int j;
-
-	const struct {
-		const char *name;
-		u32 *val_ptr;
-		const struct wled_var_cfg *cfg;
-	} u32_opts[] = {
+	struct device *dev = wled->dev;
+	const __be32 *prop_addr;
+	u32 size, val, c, string_len;
+	int rc, i, j;
+
+	const struct wled_u32_opts *u32_opts = NULL;
+	const struct wled_u32_opts wled3_opts[] = {
 		{
-			"qcom,current-boost-limit",
-			&cfg->boost_i_limit,
+			.name = "qcom,current-boost-limit",
+			.val_ptr = &cfg->boost_i_limit,
 			.cfg = &wled3_boost_i_limit_cfg,
 		},
 		{
-			"qcom,current-limit",
-			&cfg->string_i_limit,
+			.name = "qcom,current-limit",
+			.val_ptr = &cfg->string_i_limit,
 			.cfg = &wled3_string_i_limit_cfg,
 		},
 		{
-			"qcom,ovp",
-			&cfg->ovp,
+			.name = "qcom,ovp",
+			.val_ptr = &cfg->ovp,
 			.cfg = &wled3_ovp_cfg,
 		},
 		{
-			"qcom,switching-freq",
-			&cfg->switch_freq,
+			.name = "qcom,switching-freq",
+			.val_ptr = &cfg->switch_freq,
 			.cfg = &wled3_switch_freq_cfg,
 		},
 		{
-			"qcom,num-strings",
-			&cfg->num_strings,
+			.name = "qcom,num-strings",
+			.val_ptr = &cfg->num_strings,
 			.cfg = &wled3_num_strings_cfg,
 		},
 	};
-	const struct {
-		const char *name;
-		bool *val_ptr;
-	} bool_opts[] = {
+
+	const struct wled_bool_opts bool_opts[] = {
 		{ "qcom,cs-out", &cfg->cs_out_en, },
 		{ "qcom,ext-gen", &cfg->ext_gen, },
-		{ "qcom,cabc", &cfg->cabc_en, },
+		{ "qcom,cabc", &cfg->cabc, },
 	};
 
-	rc = of_property_read_u32(dev->of_node, "reg", &val);
-	if (rc || val > 0xffff) {
-		dev_err(dev, "invalid IO resources\n");
-		return rc ? rc : -EINVAL;
+	prop_addr = of_get_address(dev->of_node, 0, NULL, NULL);
+	if (!prop_addr) {
+		dev_err(wled->dev, "invalid IO resources\n");
+		return -EINVAL;
 	}
-	wled->addr = val;
+	wled->ctrl_addr = be32_to_cpu(*prop_addr);
 
 	rc = of_property_read_string(dev->of_node, "label", &wled->name);
 	if (rc)
 		wled->name = dev->of_node->name;
 
-	*cfg = wled3_config_defaults;
-	for (i = 0; i < ARRAY_SIZE(u32_opts); ++i) {
+	switch (version) {
+	case WLED3:
+		u32_opts = wled3_opts;
+		size = ARRAY_SIZE(wled3_opts);
+		*cfg = wled3_config_defaults;
+		wled->wled_set_brightness = wled3_set_brightness;
+		wled->max_string_count = 3;
+		break;
+
+	default:
+		dev_err(wled->dev, "Invalid WLED version\n");
+		break;
+	}
+
+	for (i = 0; i < size; ++i) {
 		rc = of_property_read_u32(dev->of_node, u32_opts[i].name, &val);
 		if (rc == -EINVAL) {
 			continue;
@@ -346,12 +412,15 @@  static int wled_configure(struct wled *wled, struct device *dev)
 
 		c = UINT_MAX;
 		for (j = 0; c != val; j++) {
-			c = wled3_values(u32_opts[i].cfg, j);
+			c = wled_values(u32_opts[i].cfg, j);
 			if (c == UINT_MAX) {
 				dev_err(dev, "invalid value for '%s'\n",
 					u32_opts[i].name);
 				return -EINVAL;
 			}
+
+			if (c == val)
+				break;
 		}
 
 		dev_dbg(dev, "'%s' = %u\n", u32_opts[i].name, c);
@@ -365,6 +434,15 @@  static int wled_configure(struct wled *wled, struct device *dev)
 
 	cfg->num_strings = cfg->num_strings + 1;
 
+	string_len = of_property_count_elems_of_size(dev->of_node,
+						     "qcom,enabled-strings",
+						     sizeof(u32));
+	if (string_len > 0)
+		rc = of_property_read_u32_array(dev->of_node,
+						"qcom,enabled-strings",
+						wled->cfg.enabled_strings,
+						sizeof(u32));
+
 	return 0;
 }
 
@@ -378,6 +456,7 @@  static int wled_probe(struct platform_device *pdev)
 	struct backlight_device *bl;
 	struct wled *wled;
 	struct regmap *regmap;
+	int version;
 	u32 val;
 	int rc;
 
@@ -392,22 +471,39 @@  static int wled_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	wled->regmap = regmap;
+	wled->dev = &pdev->dev;
 
-	rc = wled_configure(wled, &pdev->dev);
-	if (rc)
-		return rc;
+	version = (uintptr_t)of_device_get_match_data(&pdev->dev);
+	if (!version) {
+		dev_err(&pdev->dev, "Unknown device version\n");
+		return -ENODEV;
+	}
 
-	rc = wled_setup(wled);
+	rc = wled_configure(wled, version);
 	if (rc)
 		return rc;
 
+	switch (version) {
+	case WLED3:
+		rc = wled3_setup(wled);
+		if (rc) {
+			dev_err(&pdev->dev, "wled3_setup failed\n");
+			return rc;
+		}
+		break;
+
+	default:
+		dev_err(wled->dev, "Invalid WLED version\n");
+		break;
+	}
+
 	val = WLED_DEFAULT_BRIGHTNESS;
 	of_property_read_u32(pdev->dev.of_node, "default-brightness", &val);
 
 	memset(&props, 0, sizeof(struct backlight_properties));
 	props.type = BACKLIGHT_RAW;
 	props.brightness = val;
-	props.max_brightness = WLED3_SINK_REG_BRIGHT_MAX;
+	props.max_brightness = WLED_SINK_REG_BRIGHT_MAX;
 	bl = devm_backlight_device_register(&pdev->dev, wled->name,
 					    &pdev->dev, wled,
 					    &wled_ops, &props);
@@ -415,7 +511,7 @@  static int wled_probe(struct platform_device *pdev)
 };
 
 static const struct of_device_id wled_match_table[] = {
-	{ .compatible = "qcom,pm8941-wled" },
+	{ .compatible = "qcom,pm8941-wled", .data = (void *)3 },
 	{}
 };
 MODULE_DEVICE_TABLE(of, wled_match_table);