blob: 45eeda442c513604a03b5b3572513752f4fb1b43 [file] [log] [blame]
Thomas Gleixner97fb5e82019-05-29 07:17:58 -07001// SPDX-License-Identifier: GPL-2.0-only
Courtney Cavin93c64f12015-03-12 08:47:08 -07002/* Copyright (c) 2015, Sony Mobile Communications, AB.
Courtney Cavin93c64f12015-03-12 08:47:08 -07003 */
4
5#include <linux/kernel.h>
Bjorn Andersson7ddbc242015-07-21 17:44:49 -07006#include <linux/backlight.h>
Courtney Cavin93c64f12015-03-12 08:47:08 -07007#include <linux/module.h>
8#include <linux/of.h>
9#include <linux/of_device.h>
Kiran Gunda775d2ff2019-11-01 11:57:01 +053010#include <linux/of_address.h>
Courtney Cavin93c64f12015-03-12 08:47:08 -070011#include <linux/regmap.h>
12
Bjorn Andersson9d6c2432015-10-26 10:45:08 -070013/* From DT binding */
Kiran Gunda775d2ff2019-11-01 11:57:01 +053014#define WLED_MAX_STRINGS 4
15
Kiran Gundabb800a32019-11-01 11:57:00 +053016#define WLED_DEFAULT_BRIGHTNESS 2048
Bjorn Andersson9d6c2432015-10-26 10:45:08 -070017
Kiran Gundabb800a32019-11-01 11:57:00 +053018#define WLED3_SINK_REG_BRIGHT_MAX 0xFFF
Courtney Cavin93c64f12015-03-12 08:47:08 -070019
Kiran Gundabb800a32019-11-01 11:57:00 +053020/* WLED3 control registers */
21#define WLED3_CTRL_REG_MOD_EN 0x46
Kiran Gundabb800a32019-11-01 11:57:00 +053022#define WLED3_CTRL_REG_MOD_EN_MASK BIT(7)
Kiran Gunda775d2ff2019-11-01 11:57:01 +053023#define WLED3_CTRL_REG_MOD_EN_SHIFT 7
Courtney Cavin93c64f12015-03-12 08:47:08 -070024
Kiran Gundabb800a32019-11-01 11:57:00 +053025#define WLED3_CTRL_REG_FREQ 0x4c
Kiran Gunda775d2ff2019-11-01 11:57:01 +053026#define WLED3_CTRL_REG_FREQ_MASK GENMASK(3, 0)
Courtney Cavin93c64f12015-03-12 08:47:08 -070027
Kiran Gundabb800a32019-11-01 11:57:00 +053028#define WLED3_CTRL_REG_OVP 0x4d
Kiran Gunda775d2ff2019-11-01 11:57:01 +053029#define WLED3_CTRL_REG_OVP_MASK GENMASK(1, 0)
Courtney Cavin93c64f12015-03-12 08:47:08 -070030
Kiran Gundabb800a32019-11-01 11:57:00 +053031#define WLED3_CTRL_REG_ILIMIT 0x4e
Kiran Gunda775d2ff2019-11-01 11:57:01 +053032#define WLED3_CTRL_REG_ILIMIT_MASK GENMASK(2, 0)
Courtney Cavin93c64f12015-03-12 08:47:08 -070033
Kiran Gundabb800a32019-11-01 11:57:00 +053034/* WLED3 sink registers */
35#define WLED3_SINK_REG_SYNC 0x47
Kiran Gundabb800a32019-11-01 11:57:00 +053036#define WLED3_SINK_REG_SYNC_CLEAR 0x00
Courtney Cavin93c64f12015-03-12 08:47:08 -070037
Kiran Gundabb800a32019-11-01 11:57:00 +053038#define WLED3_SINK_REG_CURR_SINK 0x4f
Kiran Gunda775d2ff2019-11-01 11:57:01 +053039#define WLED3_SINK_REG_CURR_SINK_MASK GENMASK(7, 5)
40#define WLED3_SINK_REG_CURR_SINK_SHFT 5
Courtney Cavin93c64f12015-03-12 08:47:08 -070041
Kiran Gunda775d2ff2019-11-01 11:57:01 +053042/* WLED3 specific per-'string' registers below */
43#define WLED3_SINK_REG_BRIGHT(n) (0x40 + n)
Courtney Cavin93c64f12015-03-12 08:47:08 -070044
Kiran Gunda775d2ff2019-11-01 11:57:01 +053045#define WLED3_SINK_REG_STR_MOD_EN(n) (0x60 + (n * 0x10))
Kiran Gundabb800a32019-11-01 11:57:00 +053046#define WLED3_SINK_REG_STR_MOD_MASK BIT(7)
Courtney Cavin93c64f12015-03-12 08:47:08 -070047
Kiran Gunda775d2ff2019-11-01 11:57:01 +053048#define WLED3_SINK_REG_STR_FULL_SCALE_CURR(n) (0x62 + (n * 0x10))
49#define WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK GENMASK(4, 0)
Courtney Cavin93c64f12015-03-12 08:47:08 -070050
Kiran Gunda775d2ff2019-11-01 11:57:01 +053051#define WLED3_SINK_REG_STR_MOD_SRC(n) (0x63 + (n * 0x10))
52#define WLED3_SINK_REG_STR_MOD_SRC_MASK BIT(0)
Kiran Gundabb800a32019-11-01 11:57:00 +053053#define WLED3_SINK_REG_STR_MOD_SRC_INT 0x00
54#define WLED3_SINK_REG_STR_MOD_SRC_EXT 0x01
Courtney Cavin93c64f12015-03-12 08:47:08 -070055
Kiran Gunda775d2ff2019-11-01 11:57:01 +053056#define WLED3_SINK_REG_STR_CABC(n) (0x66 + (n * 0x10))
Kiran Gundabb800a32019-11-01 11:57:00 +053057#define WLED3_SINK_REG_STR_CABC_MASK BIT(7)
Kiran Gunda775d2ff2019-11-01 11:57:01 +053058
59struct wled_var_cfg {
60 const u32 *values;
61 u32 (*fn)(u32);
62 int size;
63};
64
65struct wled_u32_opts {
66 const char *name;
67 u32 *val_ptr;
68 const struct wled_var_cfg *cfg;
69};
70
71struct wled_bool_opts {
72 const char *name;
73 bool *val_ptr;
74};
Courtney Cavin93c64f12015-03-12 08:47:08 -070075
Kiran Gundabb800a32019-11-01 11:57:00 +053076struct wled_config {
77 u32 boost_i_limit;
Courtney Cavin93c64f12015-03-12 08:47:08 -070078 u32 ovp;
79 u32 switch_freq;
80 u32 num_strings;
Kiran Gundabb800a32019-11-01 11:57:00 +053081 u32 string_i_limit;
Kiran Gunda775d2ff2019-11-01 11:57:01 +053082 u32 enabled_strings[WLED_MAX_STRINGS];
Courtney Cavin93c64f12015-03-12 08:47:08 -070083 bool cs_out_en;
84 bool ext_gen;
Kiran Gunda775d2ff2019-11-01 11:57:01 +053085 bool cabc;
Courtney Cavin93c64f12015-03-12 08:47:08 -070086};
87
Kiran Gundabb800a32019-11-01 11:57:00 +053088struct wled {
Bjorn Andersson7ddbc242015-07-21 17:44:49 -070089 const char *name;
Kiran Gunda775d2ff2019-11-01 11:57:01 +053090 struct device *dev;
Courtney Cavin93c64f12015-03-12 08:47:08 -070091 struct regmap *regmap;
Kiran Gunda775d2ff2019-11-01 11:57:01 +053092 u16 ctrl_addr;
93 u16 max_string_count;
94 u32 brightness;
95 u32 max_brightness;
Courtney Cavin93c64f12015-03-12 08:47:08 -070096
Kiran Gundabb800a32019-11-01 11:57:00 +053097 struct wled_config cfg;
Kiran Gunda775d2ff2019-11-01 11:57:01 +053098 int (*wled_set_brightness)(struct wled *wled, u16 brightness);
Courtney Cavin93c64f12015-03-12 08:47:08 -070099};
100
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530101static int wled3_set_brightness(struct wled *wled, u16 brightness)
102{
103 int rc, i;
104 u8 v[2];
105
106 v[0] = brightness & 0xff;
107 v[1] = (brightness >> 8) & 0xf;
108
109 for (i = 0; i < wled->cfg.num_strings; ++i) {
110 rc = regmap_bulk_write(wled->regmap, wled->ctrl_addr +
111 WLED3_SINK_REG_BRIGHT(i), v, 2);
112 if (rc < 0)
113 return rc;
114 }
115
116 return 0;
117}
118
119static int wled_module_enable(struct wled *wled, int val)
120{
121 int rc;
122
123 rc = regmap_update_bits(wled->regmap, wled->ctrl_addr +
124 WLED3_CTRL_REG_MOD_EN,
125 WLED3_CTRL_REG_MOD_EN_MASK,
126 val << WLED3_CTRL_REG_MOD_EN_SHIFT);
127 return rc;
128}
129
130static int wled_sync_toggle(struct wled *wled)
131{
132 int rc;
133 unsigned int mask = GENMASK(wled->max_string_count - 1, 0);
134
135 rc = regmap_update_bits(wled->regmap,
136 wled->ctrl_addr + WLED3_SINK_REG_SYNC,
137 mask, mask);
138 if (rc < 0)
139 return rc;
140
141 rc = regmap_update_bits(wled->regmap,
142 wled->ctrl_addr + WLED3_SINK_REG_SYNC,
143 mask, WLED3_SINK_REG_SYNC_CLEAR);
144
145 return rc;
146}
147
Kiran Gundabb800a32019-11-01 11:57:00 +0530148static int wled_update_status(struct backlight_device *bl)
Courtney Cavin93c64f12015-03-12 08:47:08 -0700149{
Kiran Gundabb800a32019-11-01 11:57:00 +0530150 struct wled *wled = bl_get_data(bl);
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530151 u16 brightness = bl->props.brightness;
152 int rc = 0;
Courtney Cavin93c64f12015-03-12 08:47:08 -0700153
Bjorn Andersson7ddbc242015-07-21 17:44:49 -0700154 if (bl->props.power != FB_BLANK_UNBLANK ||
155 bl->props.fb_blank != FB_BLANK_UNBLANK ||
156 bl->props.state & BL_CORE_FBBLANK)
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530157 brightness = 0;
Courtney Cavin93c64f12015-03-12 08:47:08 -0700158
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530159 if (brightness) {
160 rc = wled->wled_set_brightness(wled, brightness);
161 if (rc < 0) {
162 dev_err(wled->dev, "wled failed to set brightness rc:%d\n",
163 rc);
Courtney Cavin93c64f12015-03-12 08:47:08 -0700164 return rc;
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530165 }
166
167 rc = wled_sync_toggle(wled);
168 if (rc < 0) {
169 dev_err(wled->dev, "wled sync failed rc:%d\n", rc);
170 return rc;
171 }
Courtney Cavin93c64f12015-03-12 08:47:08 -0700172 }
173
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530174 if (!!brightness != !!wled->brightness) {
175 rc = wled_module_enable(wled, !!brightness);
176 if (rc < 0) {
177 dev_err(wled->dev, "wled enable failed rc:%d\n", rc);
178 return rc;
179 }
180 }
Courtney Cavin93c64f12015-03-12 08:47:08 -0700181
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530182 wled->brightness = brightness;
183
Courtney Cavin93c64f12015-03-12 08:47:08 -0700184 return rc;
185}
186
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530187static int wled3_setup(struct wled *wled)
Courtney Cavin93c64f12015-03-12 08:47:08 -0700188{
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530189 u16 addr;
190 u8 sink_en = 0;
191 int rc, i, j;
Courtney Cavin93c64f12015-03-12 08:47:08 -0700192
193 rc = regmap_update_bits(wled->regmap,
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530194 wled->ctrl_addr + WLED3_CTRL_REG_OVP,
195 WLED3_CTRL_REG_OVP_MASK, wled->cfg.ovp);
Courtney Cavin93c64f12015-03-12 08:47:08 -0700196 if (rc)
197 return rc;
198
199 rc = regmap_update_bits(wled->regmap,
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530200 wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT,
201 WLED3_CTRL_REG_ILIMIT_MASK,
202 wled->cfg.boost_i_limit);
Courtney Cavin93c64f12015-03-12 08:47:08 -0700203 if (rc)
204 return rc;
205
206 rc = regmap_update_bits(wled->regmap,
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530207 wled->ctrl_addr + WLED3_CTRL_REG_FREQ,
208 WLED3_CTRL_REG_FREQ_MASK,
209 wled->cfg.switch_freq);
Courtney Cavin93c64f12015-03-12 08:47:08 -0700210 if (rc)
211 return rc;
212
Courtney Cavin93c64f12015-03-12 08:47:08 -0700213 for (i = 0; i < wled->cfg.num_strings; ++i) {
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530214 j = wled->cfg.enabled_strings[i];
215 addr = wled->ctrl_addr + WLED3_SINK_REG_STR_MOD_EN(j);
216 rc = regmap_update_bits(wled->regmap, addr,
217 WLED3_SINK_REG_STR_MOD_MASK,
218 WLED3_SINK_REG_STR_MOD_MASK);
Courtney Cavin93c64f12015-03-12 08:47:08 -0700219 if (rc)
220 return rc;
221
222 if (wled->cfg.ext_gen) {
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530223 addr = wled->ctrl_addr + WLED3_SINK_REG_STR_MOD_SRC(j);
224 rc = regmap_update_bits(wled->regmap, addr,
225 WLED3_SINK_REG_STR_MOD_SRC_MASK,
226 WLED3_SINK_REG_STR_MOD_SRC_EXT);
Courtney Cavin93c64f12015-03-12 08:47:08 -0700227 if (rc)
228 return rc;
229 }
230
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530231 addr = wled->ctrl_addr + WLED3_SINK_REG_STR_FULL_SCALE_CURR(j);
232 rc = regmap_update_bits(wled->regmap, addr,
233 WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK,
234 wled->cfg.string_i_limit);
Courtney Cavin93c64f12015-03-12 08:47:08 -0700235 if (rc)
236 return rc;
237
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530238 addr = wled->ctrl_addr + WLED3_SINK_REG_STR_CABC(j);
239 rc = regmap_update_bits(wled->regmap, addr,
240 WLED3_SINK_REG_STR_CABC_MASK,
241 wled->cfg.cabc ?
242 WLED3_SINK_REG_STR_CABC_MASK : 0);
Courtney Cavin93c64f12015-03-12 08:47:08 -0700243 if (rc)
244 return rc;
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530245
246 sink_en |= BIT(j + WLED3_SINK_REG_CURR_SINK_SHFT);
Courtney Cavin93c64f12015-03-12 08:47:08 -0700247 }
248
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530249 rc = regmap_update_bits(wled->regmap,
250 wled->ctrl_addr + WLED3_SINK_REG_CURR_SINK,
251 WLED3_SINK_REG_CURR_SINK_MASK, sink_en);
252 if (rc)
253 return rc;
254
Courtney Cavin93c64f12015-03-12 08:47:08 -0700255 return 0;
256}
257
Kiran Gundabb800a32019-11-01 11:57:00 +0530258static const struct wled_config wled3_config_defaults = {
259 .boost_i_limit = 3,
260 .string_i_limit = 20,
Courtney Cavin93c64f12015-03-12 08:47:08 -0700261 .ovp = 2,
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530262 .num_strings = 3,
Courtney Cavin93c64f12015-03-12 08:47:08 -0700263 .switch_freq = 5,
Courtney Cavin93c64f12015-03-12 08:47:08 -0700264 .cs_out_en = false,
265 .ext_gen = false,
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530266 .cabc = false,
267 .enabled_strings = {0, 1, 2, 3},
Courtney Cavin93c64f12015-03-12 08:47:08 -0700268};
269
Kiran Gundabb800a32019-11-01 11:57:00 +0530270static const u32 wled3_boost_i_limit_values[] = {
Courtney Cavin93c64f12015-03-12 08:47:08 -0700271 105, 385, 525, 805, 980, 1260, 1400, 1680,
272};
273
Kiran Gundabb800a32019-11-01 11:57:00 +0530274static const struct wled_var_cfg wled3_boost_i_limit_cfg = {
275 .values = wled3_boost_i_limit_values,
276 .size = ARRAY_SIZE(wled3_boost_i_limit_values),
Courtney Cavin93c64f12015-03-12 08:47:08 -0700277};
278
Kiran Gundabb800a32019-11-01 11:57:00 +0530279static const u32 wled3_ovp_values[] = {
Courtney Cavin93c64f12015-03-12 08:47:08 -0700280 35, 32, 29, 27,
281};
282
Kiran Gundabb800a32019-11-01 11:57:00 +0530283static const struct wled_var_cfg wled3_ovp_cfg = {
284 .values = wled3_ovp_values,
285 .size = ARRAY_SIZE(wled3_ovp_values),
Courtney Cavin93c64f12015-03-12 08:47:08 -0700286};
287
Kiran Gundabb800a32019-11-01 11:57:00 +0530288static u32 wled3_num_strings_values_fn(u32 idx)
Courtney Cavin93c64f12015-03-12 08:47:08 -0700289{
290 return idx + 1;
291}
292
Kiran Gundabb800a32019-11-01 11:57:00 +0530293static const struct wled_var_cfg wled3_num_strings_cfg = {
294 .fn = wled3_num_strings_values_fn,
Courtney Cavin93c64f12015-03-12 08:47:08 -0700295 .size = 3,
296};
297
Kiran Gundabb800a32019-11-01 11:57:00 +0530298static u32 wled3_switch_freq_values_fn(u32 idx)
Courtney Cavin93c64f12015-03-12 08:47:08 -0700299{
300 return 19200 / (2 * (1 + idx));
301}
302
Kiran Gundabb800a32019-11-01 11:57:00 +0530303static const struct wled_var_cfg wled3_switch_freq_cfg = {
304 .fn = wled3_switch_freq_values_fn,
Courtney Cavin93c64f12015-03-12 08:47:08 -0700305 .size = 16,
306};
307
Kiran Gundabb800a32019-11-01 11:57:00 +0530308static const struct wled_var_cfg wled3_string_i_limit_cfg = {
Courtney Cavin93c64f12015-03-12 08:47:08 -0700309 .size = 26,
310};
311
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530312static const struct wled_var_cfg wled3_string_cfg = {
313 .size = 8,
314};
315
316static u32 wled_values(const struct wled_var_cfg *cfg, u32 idx)
Courtney Cavin93c64f12015-03-12 08:47:08 -0700317{
318 if (idx >= cfg->size)
319 return UINT_MAX;
320 if (cfg->fn)
321 return cfg->fn(idx);
322 if (cfg->values)
323 return cfg->values[idx];
324 return idx;
325}
326
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530327static int wled_configure(struct wled *wled, int version)
Courtney Cavin93c64f12015-03-12 08:47:08 -0700328{
Kiran Gundabb800a32019-11-01 11:57:00 +0530329 struct wled_config *cfg = &wled->cfg;
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530330 struct device *dev = wled->dev;
331 const __be32 *prop_addr;
332 u32 size, val, c, string_len;
333 int rc, i, j;
Courtney Cavin93c64f12015-03-12 08:47:08 -0700334
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530335 const struct wled_u32_opts *u32_opts = NULL;
336 const struct wled_u32_opts wled3_opts[] = {
Courtney Cavin93c64f12015-03-12 08:47:08 -0700337 {
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530338 .name = "qcom,current-boost-limit",
339 .val_ptr = &cfg->boost_i_limit,
Kiran Gundabb800a32019-11-01 11:57:00 +0530340 .cfg = &wled3_boost_i_limit_cfg,
Courtney Cavin93c64f12015-03-12 08:47:08 -0700341 },
342 {
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530343 .name = "qcom,current-limit",
344 .val_ptr = &cfg->string_i_limit,
Kiran Gundabb800a32019-11-01 11:57:00 +0530345 .cfg = &wled3_string_i_limit_cfg,
Courtney Cavin93c64f12015-03-12 08:47:08 -0700346 },
347 {
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530348 .name = "qcom,ovp",
349 .val_ptr = &cfg->ovp,
Kiran Gundabb800a32019-11-01 11:57:00 +0530350 .cfg = &wled3_ovp_cfg,
Courtney Cavin93c64f12015-03-12 08:47:08 -0700351 },
352 {
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530353 .name = "qcom,switching-freq",
354 .val_ptr = &cfg->switch_freq,
Kiran Gundabb800a32019-11-01 11:57:00 +0530355 .cfg = &wled3_switch_freq_cfg,
Courtney Cavin93c64f12015-03-12 08:47:08 -0700356 },
357 {
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530358 .name = "qcom,num-strings",
359 .val_ptr = &cfg->num_strings,
Kiran Gundabb800a32019-11-01 11:57:00 +0530360 .cfg = &wled3_num_strings_cfg,
Courtney Cavin93c64f12015-03-12 08:47:08 -0700361 },
362 };
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530363
364 const struct wled_bool_opts bool_opts[] = {
Courtney Cavin93c64f12015-03-12 08:47:08 -0700365 { "qcom,cs-out", &cfg->cs_out_en, },
366 { "qcom,ext-gen", &cfg->ext_gen, },
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530367 { "qcom,cabc", &cfg->cabc, },
Courtney Cavin93c64f12015-03-12 08:47:08 -0700368 };
369
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530370 prop_addr = of_get_address(dev->of_node, 0, NULL, NULL);
371 if (!prop_addr) {
372 dev_err(wled->dev, "invalid IO resources\n");
373 return -EINVAL;
Courtney Cavin93c64f12015-03-12 08:47:08 -0700374 }
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530375 wled->ctrl_addr = be32_to_cpu(*prop_addr);
Courtney Cavin93c64f12015-03-12 08:47:08 -0700376
Bjorn Andersson7ddbc242015-07-21 17:44:49 -0700377 rc = of_property_read_string(dev->of_node, "label", &wled->name);
Courtney Cavin93c64f12015-03-12 08:47:08 -0700378 if (rc)
Rob Herringf86b7752018-08-27 20:03:42 -0500379 wled->name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node);
Courtney Cavin93c64f12015-03-12 08:47:08 -0700380
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530381 switch (version) {
382 case 3:
383 u32_opts = wled3_opts;
384 size = ARRAY_SIZE(wled3_opts);
385 *cfg = wled3_config_defaults;
386 wled->wled_set_brightness = wled3_set_brightness;
387 wled->max_string_count = 3;
388 break;
389
390 default:
391 dev_err(wled->dev, "Invalid WLED version\n");
392 return -EINVAL;
393 }
394
395 for (i = 0; i < size; ++i) {
Courtney Cavin93c64f12015-03-12 08:47:08 -0700396 rc = of_property_read_u32(dev->of_node, u32_opts[i].name, &val);
397 if (rc == -EINVAL) {
398 continue;
399 } else if (rc) {
400 dev_err(dev, "error reading '%s'\n", u32_opts[i].name);
401 return rc;
402 }
403
404 c = UINT_MAX;
405 for (j = 0; c != val; j++) {
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530406 c = wled_values(u32_opts[i].cfg, j);
Courtney Cavin93c64f12015-03-12 08:47:08 -0700407 if (c == UINT_MAX) {
408 dev_err(dev, "invalid value for '%s'\n",
409 u32_opts[i].name);
410 return -EINVAL;
411 }
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530412
413 if (c == val)
414 break;
Courtney Cavin93c64f12015-03-12 08:47:08 -0700415 }
416
417 dev_dbg(dev, "'%s' = %u\n", u32_opts[i].name, c);
418 *u32_opts[i].val_ptr = j;
419 }
420
421 for (i = 0; i < ARRAY_SIZE(bool_opts); ++i) {
422 if (of_property_read_bool(dev->of_node, bool_opts[i].name))
423 *bool_opts[i].val_ptr = true;
424 }
425
426 cfg->num_strings = cfg->num_strings + 1;
427
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530428 string_len = of_property_count_elems_of_size(dev->of_node,
429 "qcom,enabled-strings",
430 sizeof(u32));
431 if (string_len > 0)
432 of_property_read_u32_array(dev->of_node,
433 "qcom,enabled-strings",
434 wled->cfg.enabled_strings,
435 sizeof(u32));
436
Courtney Cavin93c64f12015-03-12 08:47:08 -0700437 return 0;
438}
439
Kiran Gundabb800a32019-11-01 11:57:00 +0530440static const struct backlight_ops wled_ops = {
441 .update_status = wled_update_status,
Bjorn Andersson7ddbc242015-07-21 17:44:49 -0700442};
443
Kiran Gundabb800a32019-11-01 11:57:00 +0530444static int wled_probe(struct platform_device *pdev)
Courtney Cavin93c64f12015-03-12 08:47:08 -0700445{
Bjorn Andersson7ddbc242015-07-21 17:44:49 -0700446 struct backlight_properties props;
447 struct backlight_device *bl;
Kiran Gundabb800a32019-11-01 11:57:00 +0530448 struct wled *wled;
Courtney Cavin93c64f12015-03-12 08:47:08 -0700449 struct regmap *regmap;
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530450 int version;
Bjorn Andersson9d6c2432015-10-26 10:45:08 -0700451 u32 val;
Courtney Cavin93c64f12015-03-12 08:47:08 -0700452 int rc;
453
454 regmap = dev_get_regmap(pdev->dev.parent, NULL);
455 if (!regmap) {
456 dev_err(&pdev->dev, "Unable to get regmap\n");
457 return -EINVAL;
458 }
459
460 wled = devm_kzalloc(&pdev->dev, sizeof(*wled), GFP_KERNEL);
461 if (!wled)
462 return -ENOMEM;
463
464 wled->regmap = regmap;
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530465 wled->dev = &pdev->dev;
Courtney Cavin93c64f12015-03-12 08:47:08 -0700466
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530467 version = (uintptr_t)of_device_get_match_data(&pdev->dev);
468 if (!version) {
469 dev_err(&pdev->dev, "Unknown device version\n");
470 return -ENODEV;
471 }
472
473 rc = wled_configure(wled, version);
Courtney Cavin93c64f12015-03-12 08:47:08 -0700474 if (rc)
475 return rc;
476
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530477 switch (version) {
478 case 3:
479 rc = wled3_setup(wled);
480 if (rc) {
481 dev_err(&pdev->dev, "wled3_setup failed\n");
482 return rc;
483 }
484 break;
485
486 default:
487 dev_err(wled->dev, "Invalid WLED version\n");
488 break;
489 }
Courtney Cavin93c64f12015-03-12 08:47:08 -0700490
Kiran Gundabb800a32019-11-01 11:57:00 +0530491 val = WLED_DEFAULT_BRIGHTNESS;
Bjorn Andersson9d6c2432015-10-26 10:45:08 -0700492 of_property_read_u32(pdev->dev.of_node, "default-brightness", &val);
493
Bjorn Andersson7ddbc242015-07-21 17:44:49 -0700494 memset(&props, 0, sizeof(struct backlight_properties));
495 props.type = BACKLIGHT_RAW;
Bjorn Andersson9d6c2432015-10-26 10:45:08 -0700496 props.brightness = val;
Kiran Gundabb800a32019-11-01 11:57:00 +0530497 props.max_brightness = WLED3_SINK_REG_BRIGHT_MAX;
Bjorn Andersson7ddbc242015-07-21 17:44:49 -0700498 bl = devm_backlight_device_register(&pdev->dev, wled->name,
499 &pdev->dev, wled,
Kiran Gundabb800a32019-11-01 11:57:00 +0530500 &wled_ops, &props);
kbuild test robotfc181112015-10-27 02:26:11 +0800501 return PTR_ERR_OR_ZERO(bl);
Courtney Cavin93c64f12015-03-12 08:47:08 -0700502};
503
Kiran Gundabb800a32019-11-01 11:57:00 +0530504static const struct of_device_id wled_match_table[] = {
Kiran Gunda775d2ff2019-11-01 11:57:01 +0530505 { .compatible = "qcom,pm8941-wled", .data = (void *)3 },
Courtney Cavin93c64f12015-03-12 08:47:08 -0700506 {}
507};
Kiran Gundabb800a32019-11-01 11:57:00 +0530508MODULE_DEVICE_TABLE(of, wled_match_table);
Courtney Cavin93c64f12015-03-12 08:47:08 -0700509
Kiran Gundabb800a32019-11-01 11:57:00 +0530510static struct platform_driver wled_driver = {
511 .probe = wled_probe,
Courtney Cavin93c64f12015-03-12 08:47:08 -0700512 .driver = {
Kiran Gundabb800a32019-11-01 11:57:00 +0530513 .name = "qcom,wled",
514 .of_match_table = wled_match_table,
Courtney Cavin93c64f12015-03-12 08:47:08 -0700515 },
516};
517
Kiran Gundabb800a32019-11-01 11:57:00 +0530518module_platform_driver(wled_driver);
Courtney Cavin93c64f12015-03-12 08:47:08 -0700519
Kiran Gundabb800a32019-11-01 11:57:00 +0530520MODULE_DESCRIPTION("Qualcomm WLED driver");
Courtney Cavin93c64f12015-03-12 08:47:08 -0700521MODULE_LICENSE("GPL v2");