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.h>
12 #include <linux/init.h>
13 #include <linux/kernel.h>
14 #include <linux/module.h>
16 #include <linux/of_gpio.h>
17 #include <linux/platform_device.h>
18 #include <linux/regmap.h>
19 #include <linux/platform_data/sky81452-backlight.h>
20 #include <linux/slab.h>
23 #define SKY81452_REG0 0x00
24 #define SKY81452_REG1 0x01
25 #define SKY81452_REG2 0x02
26 #define SKY81452_REG4 0x04
27 #define SKY81452_REG5 0x05
30 #define SKY81452_CS 0xFF
31 #define SKY81452_EN 0x3F
32 #define SKY81452_IGPW 0x20
33 #define SKY81452_PWMMD 0x10
34 #define SKY81452_PHASE 0x08
35 #define SKY81452_ILIM 0x04
36 #define SKY81452_VSHRT 0x03
37 #define SKY81452_OCP 0x80
38 #define SKY81452_OTMP 0x40
39 #define SKY81452_SHRT 0x3F
40 #define SKY81452_OPN 0x3F
42 #define SKY81452_DEFAULT_NAME "lcd-backlight"
43 #define SKY81452_MAX_BRIGHTNESS (SKY81452_CS + 1)
45 #define CTZ(b) __builtin_ctz(b)
47 static int sky81452_bl_update_status(struct backlight_device
*bd
)
49 const struct sky81452_bl_platform_data
*pdata
=
50 dev_get_platdata(bd
->dev
.parent
);
51 const unsigned int brightness
= (unsigned int)bd
->props
.brightness
;
52 struct regmap
*regmap
= bl_get_data(bd
);
56 ret
= regmap_write(regmap
, SKY81452_REG0
, brightness
- 1);
60 return regmap_update_bits(regmap
, SKY81452_REG1
, SKY81452_EN
,
61 pdata
->enable
<< CTZ(SKY81452_EN
));
64 return regmap_update_bits(regmap
, SKY81452_REG1
, SKY81452_EN
, 0);
67 static const struct backlight_ops sky81452_bl_ops
= {
68 .update_status
= sky81452_bl_update_status
,
71 static ssize_t
sky81452_bl_store_enable(struct device
*dev
,
72 struct device_attribute
*attr
, const char *buf
, size_t count
)
74 struct regmap
*regmap
= bl_get_data(to_backlight_device(dev
));
78 ret
= kstrtoul(buf
, 16, &value
);
82 ret
= regmap_update_bits(regmap
, SKY81452_REG1
, SKY81452_EN
,
83 value
<< CTZ(SKY81452_EN
));
90 static ssize_t
sky81452_bl_show_open_short(struct device
*dev
,
91 struct device_attribute
*attr
, char *buf
)
93 struct regmap
*regmap
= bl_get_data(to_backlight_device(dev
));
94 unsigned int reg
, value
= 0;
98 reg
= !strcmp(attr
->attr
.name
, "open") ? SKY81452_REG5
: SKY81452_REG4
;
99 ret
= regmap_read(regmap
, reg
, &value
);
103 if (value
& SKY81452_SHRT
) {
105 for (i
= 0; i
< 6; i
++) {
107 sprintf(tmp
, "%d ", i
+ 1);
114 strcpy(buf
, "none\n");
120 static ssize_t
sky81452_bl_show_fault(struct device
*dev
,
121 struct device_attribute
*attr
, char *buf
)
123 struct regmap
*regmap
= bl_get_data(to_backlight_device(dev
));
124 unsigned int value
= 0;
127 ret
= regmap_read(regmap
, SKY81452_REG4
, &value
);
133 if (value
& SKY81452_OCP
)
134 strcat(buf
, "over-current ");
136 if (value
& SKY81452_OTMP
)
137 strcat(buf
, "over-temperature");
143 static DEVICE_ATTR(enable
, S_IWGRP
| S_IWUSR
, NULL
, sky81452_bl_store_enable
);
144 static DEVICE_ATTR(open
, S_IRUGO
, sky81452_bl_show_open_short
, NULL
);
145 static DEVICE_ATTR(short, S_IRUGO
, sky81452_bl_show_open_short
, NULL
);
146 static DEVICE_ATTR(fault
, S_IRUGO
, sky81452_bl_show_fault
, NULL
);
148 static struct attribute
*sky81452_bl_attribute
[] = {
149 &dev_attr_enable
.attr
,
151 &dev_attr_short
.attr
,
152 &dev_attr_fault
.attr
,
156 static const struct attribute_group sky81452_bl_attr_group
= {
157 .attrs
= sky81452_bl_attribute
,
161 static struct sky81452_bl_platform_data
*sky81452_bl_parse_dt(
164 struct device_node
*np
= of_node_get(dev
->of_node
);
165 struct sky81452_bl_platform_data
*pdata
;
167 unsigned int sources
[6];
171 dev_err(dev
, "backlight node not found.\n");
172 return ERR_PTR(-ENODATA
);
175 pdata
= devm_kzalloc(dev
, sizeof(*pdata
), GFP_KERNEL
);
178 return ERR_PTR(-ENOMEM
);
181 of_property_read_string(np
, "name", &pdata
->name
);
182 pdata
->ignore_pwm
= of_property_read_bool(np
, "skyworks,ignore-pwm");
183 pdata
->dpwm_mode
= of_property_read_bool(np
, "skyworks,dpwm-mode");
184 pdata
->phase_shift
= of_property_read_bool(np
, "skyworks,phase-shift");
185 pdata
->gpio_enable
= of_get_gpio(np
, 0);
187 ret
= of_property_count_u32_elems(np
, "led-sources");
189 pdata
->enable
= SKY81452_EN
>> CTZ(SKY81452_EN
);
195 ret
= of_property_read_u32_array(np
, "led-sources", sources
,
198 dev_err(dev
, "led-sources node is invalid.\n");
199 return ERR_PTR(-EINVAL
);
204 pdata
->enable
|= (1 << sources
[num_entry
]);
207 ret
= of_property_read_u32(np
,
208 "skyworks,short-detection-threshold-volt",
209 &pdata
->short_detection_threshold
);
211 pdata
->short_detection_threshold
= 7;
213 ret
= of_property_read_u32(np
, "skyworks,current-limit-mA",
214 &pdata
->boost_current_limit
);
216 pdata
->boost_current_limit
= 2750;
222 static struct sky81452_bl_platform_data
*sky81452_bl_parse_dt(
225 return ERR_PTR(-EINVAL
);
229 static int sky81452_bl_init_device(struct regmap
*regmap
,
230 struct sky81452_bl_platform_data
*pdata
)
234 value
= pdata
->ignore_pwm
? SKY81452_IGPW
: 0;
235 value
|= pdata
->dpwm_mode
? SKY81452_PWMMD
: 0;
236 value
|= pdata
->phase_shift
? 0 : SKY81452_PHASE
;
238 if (pdata
->boost_current_limit
== 2300)
239 value
|= SKY81452_ILIM
;
240 else if (pdata
->boost_current_limit
!= 2750)
243 if (pdata
->short_detection_threshold
< 4 ||
244 pdata
->short_detection_threshold
> 7)
246 value
|= (7 - pdata
->short_detection_threshold
) << CTZ(SKY81452_VSHRT
);
248 return regmap_write(regmap
, SKY81452_REG2
, value
);
251 static int sky81452_bl_probe(struct platform_device
*pdev
)
253 struct device
*dev
= &pdev
->dev
;
254 struct regmap
*regmap
= dev_get_drvdata(dev
->parent
);
255 struct sky81452_bl_platform_data
*pdata
= dev_get_platdata(dev
);
256 struct backlight_device
*bd
;
257 struct backlight_properties props
;
262 pdata
= sky81452_bl_parse_dt(dev
);
264 return PTR_ERR(pdata
);
267 if (gpio_is_valid(pdata
->gpio_enable
)) {
268 ret
= devm_gpio_request_one(dev
, pdata
->gpio_enable
,
269 GPIOF_OUT_INIT_HIGH
, "sky81452-en");
271 dev_err(dev
, "failed to request GPIO. err=%d\n", ret
);
276 ret
= sky81452_bl_init_device(regmap
, pdata
);
278 dev_err(dev
, "failed to initialize. err=%d\n", ret
);
282 memset(&props
, 0, sizeof(props
));
283 props
.max_brightness
= SKY81452_MAX_BRIGHTNESS
,
284 name
= pdata
->name
? pdata
->name
: SKY81452_DEFAULT_NAME
;
285 bd
= devm_backlight_device_register(dev
, name
, dev
, regmap
,
286 &sky81452_bl_ops
, &props
);
288 dev_err(dev
, "failed to register. err=%ld\n", PTR_ERR(bd
));
292 platform_set_drvdata(pdev
, bd
);
294 ret
= sysfs_create_group(&bd
->dev
.kobj
, &sky81452_bl_attr_group
);
296 dev_err(dev
, "failed to create attribute. err=%d\n", ret
);
303 static int sky81452_bl_remove(struct platform_device
*pdev
)
305 const struct sky81452_bl_platform_data
*pdata
=
306 dev_get_platdata(&pdev
->dev
);
307 struct backlight_device
*bd
= platform_get_drvdata(pdev
);
309 sysfs_remove_group(&bd
->dev
.kobj
, &sky81452_bl_attr_group
);
311 bd
->props
.power
= FB_BLANK_UNBLANK
;
312 bd
->props
.brightness
= 0;
313 backlight_update_status(bd
);
315 if (gpio_is_valid(pdata
->gpio_enable
))
316 gpio_set_value_cansleep(pdata
->gpio_enable
, 0);
322 static const struct of_device_id sky81452_bl_of_match
[] = {
323 { .compatible
= "skyworks,sky81452-backlight", },
326 MODULE_DEVICE_TABLE(of
, sky81452_bl_of_match
);
329 static struct platform_driver sky81452_bl_driver
= {
331 .name
= "sky81452-backlight",
332 .of_match_table
= of_match_ptr(sky81452_bl_of_match
),
334 .probe
= sky81452_bl_probe
,
335 .remove
= sky81452_bl_remove
,
338 module_platform_driver(sky81452_bl_driver
);
340 MODULE_DESCRIPTION("Skyworks SKY81452 backlight driver");
341 MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@skyworksinc.com>");
342 MODULE_LICENSE("GPL v2");