1 // SPDX-License-Identifier: GPL-2.0-only
3 * sky81452-backlight.c SKY81452 backlight driver
5 * Copyright 2014 Skyworks Solutions Inc.
6 * Author : Gyungoh Yoo <jack.yoo@skyworksinc.com>
9 #include <linux/backlight.h>
10 #include <linux/err.h>
11 #include <linux/gpio/consumer.h>
12 #include <linux/init.h>
13 #include <linux/kernel.h>
14 #include <linux/module.h>
16 #include <linux/platform_device.h>
17 #include <linux/regmap.h>
18 #include <linux/slab.h>
21 #define SKY81452_REG0 0x00
22 #define SKY81452_REG1 0x01
23 #define SKY81452_REG2 0x02
24 #define SKY81452_REG4 0x04
25 #define SKY81452_REG5 0x05
28 #define SKY81452_CS 0xFF
29 #define SKY81452_EN 0x3F
30 #define SKY81452_IGPW 0x20
31 #define SKY81452_PWMMD 0x10
32 #define SKY81452_PHASE 0x08
33 #define SKY81452_ILIM 0x04
34 #define SKY81452_VSHRT 0x03
35 #define SKY81452_OCP 0x80
36 #define SKY81452_OTMP 0x40
37 #define SKY81452_SHRT 0x3F
38 #define SKY81452_OPN 0x3F
40 #define SKY81452_DEFAULT_NAME "lcd-backlight"
41 #define SKY81452_MAX_BRIGHTNESS (SKY81452_CS + 1)
44 * struct sky81452_bl_platform_data - backlight platform data
45 * @name: backlight driver name.
46 * If it is not defined, default name is lcd-backlight.
47 * @gpiod_enable:GPIO descriptor which control EN pin
48 * @enable: Enable mask for current sink channel 1, 2, 3, 4, 5 and 6.
49 * @ignore_pwm: true if DPWMI should be ignored.
50 * @dpwm_mode: true is DPWM dimming mode, otherwise Analog dimming mode.
51 * @phase_shift:true is phase shift mode.
52 * @short_detection_threshold: It should be one of 4, 5, 6 and 7V.
53 * @boost_current_limit: It should be one of 2300, 2750mA.
55 struct sky81452_bl_platform_data
{
57 struct gpio_desc
*gpiod_enable
;
62 unsigned int short_detection_threshold
;
63 unsigned int boost_current_limit
;
66 #define CTZ(b) __builtin_ctz(b)
68 static int sky81452_bl_update_status(struct backlight_device
*bd
)
70 const struct sky81452_bl_platform_data
*pdata
=
71 dev_get_platdata(bd
->dev
.parent
);
72 const unsigned int brightness
= (unsigned int)bd
->props
.brightness
;
73 struct regmap
*regmap
= bl_get_data(bd
);
77 ret
= regmap_write(regmap
, SKY81452_REG0
, brightness
- 1);
81 return regmap_update_bits(regmap
, SKY81452_REG1
, SKY81452_EN
,
82 pdata
->enable
<< CTZ(SKY81452_EN
));
85 return regmap_update_bits(regmap
, SKY81452_REG1
, SKY81452_EN
, 0);
88 static const struct backlight_ops sky81452_bl_ops
= {
89 .update_status
= sky81452_bl_update_status
,
92 static ssize_t
sky81452_bl_store_enable(struct device
*dev
,
93 struct device_attribute
*attr
, const char *buf
, size_t count
)
95 struct regmap
*regmap
= bl_get_data(to_backlight_device(dev
));
99 ret
= kstrtoul(buf
, 16, &value
);
103 ret
= regmap_update_bits(regmap
, SKY81452_REG1
, SKY81452_EN
,
104 value
<< CTZ(SKY81452_EN
));
111 static ssize_t
sky81452_bl_show_open_short(struct device
*dev
,
112 struct device_attribute
*attr
, char *buf
)
114 struct regmap
*regmap
= bl_get_data(to_backlight_device(dev
));
115 unsigned int reg
, value
= 0;
119 reg
= !strcmp(attr
->attr
.name
, "open") ? SKY81452_REG5
: SKY81452_REG4
;
120 ret
= regmap_read(regmap
, reg
, &value
);
124 if (value
& SKY81452_SHRT
) {
126 for (i
= 0; i
< 6; i
++) {
128 sprintf(tmp
, "%d ", i
+ 1);
135 strcpy(buf
, "none\n");
141 static ssize_t
sky81452_bl_show_fault(struct device
*dev
,
142 struct device_attribute
*attr
, char *buf
)
144 struct regmap
*regmap
= bl_get_data(to_backlight_device(dev
));
145 unsigned int value
= 0;
148 ret
= regmap_read(regmap
, SKY81452_REG4
, &value
);
154 if (value
& SKY81452_OCP
)
155 strcat(buf
, "over-current ");
157 if (value
& SKY81452_OTMP
)
158 strcat(buf
, "over-temperature");
164 static DEVICE_ATTR(enable
, S_IWGRP
| S_IWUSR
, NULL
, sky81452_bl_store_enable
);
165 static DEVICE_ATTR(open
, S_IRUGO
, sky81452_bl_show_open_short
, NULL
);
166 static DEVICE_ATTR(short, S_IRUGO
, sky81452_bl_show_open_short
, NULL
);
167 static DEVICE_ATTR(fault
, S_IRUGO
, sky81452_bl_show_fault
, NULL
);
169 static struct attribute
*sky81452_bl_attribute
[] = {
170 &dev_attr_enable
.attr
,
172 &dev_attr_short
.attr
,
173 &dev_attr_fault
.attr
,
177 static const struct attribute_group sky81452_bl_attr_group
= {
178 .attrs
= sky81452_bl_attribute
,
182 static struct sky81452_bl_platform_data
*sky81452_bl_parse_dt(
185 struct device_node
*np
= dev
->of_node
;
186 struct sky81452_bl_platform_data
*pdata
;
188 unsigned int sources
[6];
192 dev_err(dev
, "backlight node not found.\n");
193 return ERR_PTR(-ENODATA
);
196 pdata
= devm_kzalloc(dev
, sizeof(*pdata
), GFP_KERNEL
);
198 return ERR_PTR(-ENOMEM
);
200 of_property_read_string(np
, "name", &pdata
->name
);
201 pdata
->ignore_pwm
= of_property_read_bool(np
, "skyworks,ignore-pwm");
202 pdata
->dpwm_mode
= of_property_read_bool(np
, "skyworks,dpwm-mode");
203 pdata
->phase_shift
= of_property_read_bool(np
, "skyworks,phase-shift");
204 pdata
->gpiod_enable
= devm_gpiod_get_optional(dev
, NULL
, GPIOD_OUT_HIGH
);
206 ret
= of_property_count_u32_elems(np
, "led-sources");
208 pdata
->enable
= SKY81452_EN
>> CTZ(SKY81452_EN
);
214 ret
= of_property_read_u32_array(np
, "led-sources", sources
,
217 dev_err(dev
, "led-sources node is invalid.\n");
218 return ERR_PTR(-EINVAL
);
223 pdata
->enable
|= (1 << sources
[num_entry
]);
226 ret
= of_property_read_u32(np
,
227 "skyworks,short-detection-threshold-volt",
228 &pdata
->short_detection_threshold
);
230 pdata
->short_detection_threshold
= 7;
232 ret
= of_property_read_u32(np
, "skyworks,current-limit-mA",
233 &pdata
->boost_current_limit
);
235 pdata
->boost_current_limit
= 2750;
240 static struct sky81452_bl_platform_data
*sky81452_bl_parse_dt(
243 return ERR_PTR(-EINVAL
);
247 static int sky81452_bl_init_device(struct regmap
*regmap
,
248 struct sky81452_bl_platform_data
*pdata
)
252 value
= pdata
->ignore_pwm
? SKY81452_IGPW
: 0;
253 value
|= pdata
->dpwm_mode
? SKY81452_PWMMD
: 0;
254 value
|= pdata
->phase_shift
? 0 : SKY81452_PHASE
;
256 if (pdata
->boost_current_limit
== 2300)
257 value
|= SKY81452_ILIM
;
258 else if (pdata
->boost_current_limit
!= 2750)
261 if (pdata
->short_detection_threshold
< 4 ||
262 pdata
->short_detection_threshold
> 7)
264 value
|= (7 - pdata
->short_detection_threshold
) << CTZ(SKY81452_VSHRT
);
266 return regmap_write(regmap
, SKY81452_REG2
, value
);
269 static int sky81452_bl_probe(struct platform_device
*pdev
)
271 struct device
*dev
= &pdev
->dev
;
272 struct regmap
*regmap
= dev_get_drvdata(dev
->parent
);
273 struct sky81452_bl_platform_data
*pdata
;
274 struct backlight_device
*bd
;
275 struct backlight_properties props
;
279 pdata
= sky81452_bl_parse_dt(dev
);
281 return PTR_ERR(pdata
);
283 ret
= sky81452_bl_init_device(regmap
, pdata
);
285 dev_err(dev
, "failed to initialize. err=%d\n", ret
);
289 memset(&props
, 0, sizeof(props
));
290 props
.max_brightness
= SKY81452_MAX_BRIGHTNESS
;
291 name
= pdata
->name
? pdata
->name
: SKY81452_DEFAULT_NAME
;
292 bd
= devm_backlight_device_register(dev
, name
, dev
, regmap
,
293 &sky81452_bl_ops
, &props
);
295 dev_err(dev
, "failed to register. err=%ld\n", PTR_ERR(bd
));
299 platform_set_drvdata(pdev
, bd
);
301 ret
= sysfs_create_group(&bd
->dev
.kobj
, &sky81452_bl_attr_group
);
303 dev_err(dev
, "failed to create attribute. err=%d\n", ret
);
310 static void sky81452_bl_remove(struct platform_device
*pdev
)
312 const struct sky81452_bl_platform_data
*pdata
=
313 dev_get_platdata(&pdev
->dev
);
314 struct backlight_device
*bd
= platform_get_drvdata(pdev
);
316 sysfs_remove_group(&bd
->dev
.kobj
, &sky81452_bl_attr_group
);
318 bd
->props
.power
= BACKLIGHT_POWER_ON
;
319 bd
->props
.brightness
= 0;
320 backlight_update_status(bd
);
322 if (pdata
->gpiod_enable
)
323 gpiod_set_value_cansleep(pdata
->gpiod_enable
, 0);
327 static const struct of_device_id sky81452_bl_of_match
[] = {
328 { .compatible
= "skyworks,sky81452-backlight", },
331 MODULE_DEVICE_TABLE(of
, sky81452_bl_of_match
);
334 static struct platform_driver sky81452_bl_driver
= {
336 .name
= "sky81452-backlight",
337 .of_match_table
= of_match_ptr(sky81452_bl_of_match
),
339 .probe
= sky81452_bl_probe
,
340 .remove
= sky81452_bl_remove
,
343 module_platform_driver(sky81452_bl_driver
);
345 MODULE_DESCRIPTION("Skyworks SKY81452 backlight driver");
346 MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@skyworksinc.com>");
347 MODULE_LICENSE("GPL v2");