1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * lm3533-bl.c -- LM3533 Backlight driver
4  *
5  * Copyright (C) 2011-2012 Texas Instruments
6  *
7  * Author: Johan Hovold <jhovold@gmail.com>
8  */
9 
10 #include <linux/module.h>
11 #include <linux/init.h>
12 #include <linux/platform_device.h>
13 #include <linux/backlight.h>
14 #include <linux/fb.h>
15 #include <linux/slab.h>
16 
17 #include <linux/mfd/lm3533.h>
18 
19 
20 #define LM3533_HVCTRLBANK_COUNT		2
21 #define LM3533_BL_MAX_BRIGHTNESS	255
22 
23 #define LM3533_REG_CTRLBANK_AB_BCONF	0x1a
24 
25 
26 struct lm3533_bl {
27 	struct lm3533 *lm3533;
28 	struct lm3533_ctrlbank cb;
29 	struct backlight_device *bd;
30 	int id;
31 };
32 
33 
lm3533_bl_get_ctrlbank_id(struct lm3533_bl * bl)34 static inline int lm3533_bl_get_ctrlbank_id(struct lm3533_bl *bl)
35 {
36 	return bl->id;
37 }
38 
lm3533_bl_update_status(struct backlight_device * bd)39 static int lm3533_bl_update_status(struct backlight_device *bd)
40 {
41 	struct lm3533_bl *bl = bl_get_data(bd);
42 
43 	return lm3533_ctrlbank_set_brightness(&bl->cb, backlight_get_brightness(bd));
44 }
45 
lm3533_bl_get_brightness(struct backlight_device * bd)46 static int lm3533_bl_get_brightness(struct backlight_device *bd)
47 {
48 	struct lm3533_bl *bl = bl_get_data(bd);
49 	u8 val;
50 	int ret;
51 
52 	ret = lm3533_ctrlbank_get_brightness(&bl->cb, &val);
53 	if (ret)
54 		return ret;
55 
56 	return val;
57 }
58 
59 static const struct backlight_ops lm3533_bl_ops = {
60 	.get_brightness	= lm3533_bl_get_brightness,
61 	.update_status	= lm3533_bl_update_status,
62 };
63 
show_id(struct device * dev,struct device_attribute * attr,char * buf)64 static ssize_t show_id(struct device *dev,
65 				struct device_attribute *attr, char *buf)
66 {
67 	struct lm3533_bl *bl = dev_get_drvdata(dev);
68 
69 	return scnprintf(buf, PAGE_SIZE, "%d\n", bl->id);
70 }
71 
show_als_channel(struct device * dev,struct device_attribute * attr,char * buf)72 static ssize_t show_als_channel(struct device *dev,
73 				struct device_attribute *attr, char *buf)
74 {
75 	struct lm3533_bl *bl = dev_get_drvdata(dev);
76 	unsigned channel = lm3533_bl_get_ctrlbank_id(bl);
77 
78 	return scnprintf(buf, PAGE_SIZE, "%u\n", channel);
79 }
80 
show_als_en(struct device * dev,struct device_attribute * attr,char * buf)81 static ssize_t show_als_en(struct device *dev,
82 				struct device_attribute *attr, char *buf)
83 {
84 	struct lm3533_bl *bl = dev_get_drvdata(dev);
85 	int ctrlbank = lm3533_bl_get_ctrlbank_id(bl);
86 	u8 val;
87 	u8 mask;
88 	bool enable;
89 	int ret;
90 
91 	ret = lm3533_read(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, &val);
92 	if (ret)
93 		return ret;
94 
95 	mask = 1 << (2 * ctrlbank);
96 	enable = val & mask;
97 
98 	return scnprintf(buf, PAGE_SIZE, "%d\n", enable);
99 }
100 
store_als_en(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)101 static ssize_t store_als_en(struct device *dev,
102 					struct device_attribute *attr,
103 					const char *buf, size_t len)
104 {
105 	struct lm3533_bl *bl = dev_get_drvdata(dev);
106 	int ctrlbank = lm3533_bl_get_ctrlbank_id(bl);
107 	int enable;
108 	u8 val;
109 	u8 mask;
110 	int ret;
111 
112 	if (kstrtoint(buf, 0, &enable))
113 		return -EINVAL;
114 
115 	mask = 1 << (2 * ctrlbank);
116 
117 	if (enable)
118 		val = mask;
119 	else
120 		val = 0;
121 
122 	ret = lm3533_update(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, val,
123 									mask);
124 	if (ret)
125 		return ret;
126 
127 	return len;
128 }
129 
show_linear(struct device * dev,struct device_attribute * attr,char * buf)130 static ssize_t show_linear(struct device *dev,
131 				struct device_attribute *attr, char *buf)
132 {
133 	struct lm3533_bl *bl = dev_get_drvdata(dev);
134 	u8 val;
135 	u8 mask;
136 	int linear;
137 	int ret;
138 
139 	ret = lm3533_read(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, &val);
140 	if (ret)
141 		return ret;
142 
143 	mask = 1 << (2 * lm3533_bl_get_ctrlbank_id(bl) + 1);
144 
145 	if (val & mask)
146 		linear = 1;
147 	else
148 		linear = 0;
149 
150 	return scnprintf(buf, PAGE_SIZE, "%x\n", linear);
151 }
152 
store_linear(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)153 static ssize_t store_linear(struct device *dev,
154 					struct device_attribute *attr,
155 					const char *buf, size_t len)
156 {
157 	struct lm3533_bl *bl = dev_get_drvdata(dev);
158 	unsigned long linear;
159 	u8 mask;
160 	u8 val;
161 	int ret;
162 
163 	if (kstrtoul(buf, 0, &linear))
164 		return -EINVAL;
165 
166 	mask = 1 << (2 * lm3533_bl_get_ctrlbank_id(bl) + 1);
167 
168 	if (linear)
169 		val = mask;
170 	else
171 		val = 0;
172 
173 	ret = lm3533_update(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, val,
174 									mask);
175 	if (ret)
176 		return ret;
177 
178 	return len;
179 }
180 
show_pwm(struct device * dev,struct device_attribute * attr,char * buf)181 static ssize_t show_pwm(struct device *dev,
182 					struct device_attribute *attr,
183 					char *buf)
184 {
185 	struct lm3533_bl *bl = dev_get_drvdata(dev);
186 	u8 val;
187 	int ret;
188 
189 	ret = lm3533_ctrlbank_get_pwm(&bl->cb, &val);
190 	if (ret)
191 		return ret;
192 
193 	return scnprintf(buf, PAGE_SIZE, "%u\n", val);
194 }
195 
store_pwm(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)196 static ssize_t store_pwm(struct device *dev,
197 					struct device_attribute *attr,
198 					const char *buf, size_t len)
199 {
200 	struct lm3533_bl *bl = dev_get_drvdata(dev);
201 	u8 val;
202 	int ret;
203 
204 	if (kstrtou8(buf, 0, &val))
205 		return -EINVAL;
206 
207 	ret = lm3533_ctrlbank_set_pwm(&bl->cb, val);
208 	if (ret)
209 		return ret;
210 
211 	return len;
212 }
213 
214 static LM3533_ATTR_RO(als_channel);
215 static LM3533_ATTR_RW(als_en);
216 static LM3533_ATTR_RO(id);
217 static LM3533_ATTR_RW(linear);
218 static LM3533_ATTR_RW(pwm);
219 
220 static struct attribute *lm3533_bl_attributes[] = {
221 	&dev_attr_als_channel.attr,
222 	&dev_attr_als_en.attr,
223 	&dev_attr_id.attr,
224 	&dev_attr_linear.attr,
225 	&dev_attr_pwm.attr,
226 	NULL,
227 };
228 
lm3533_bl_attr_is_visible(struct kobject * kobj,struct attribute * attr,int n)229 static umode_t lm3533_bl_attr_is_visible(struct kobject *kobj,
230 					     struct attribute *attr, int n)
231 {
232 	struct device *dev = kobj_to_dev(kobj);
233 	struct lm3533_bl *bl = dev_get_drvdata(dev);
234 	umode_t mode = attr->mode;
235 
236 	if (attr == &dev_attr_als_channel.attr ||
237 					attr == &dev_attr_als_en.attr) {
238 		if (!bl->lm3533->have_als)
239 			mode = 0;
240 	}
241 
242 	return mode;
243 };
244 
245 static struct attribute_group lm3533_bl_attribute_group = {
246 	.is_visible	= lm3533_bl_attr_is_visible,
247 	.attrs		= lm3533_bl_attributes
248 };
249 
lm3533_bl_setup(struct lm3533_bl * bl,struct lm3533_bl_platform_data * pdata)250 static int lm3533_bl_setup(struct lm3533_bl *bl,
251 					struct lm3533_bl_platform_data *pdata)
252 {
253 	int ret;
254 
255 	ret = lm3533_ctrlbank_set_max_current(&bl->cb, pdata->max_current);
256 	if (ret)
257 		return ret;
258 
259 	return lm3533_ctrlbank_set_pwm(&bl->cb, pdata->pwm);
260 }
261 
lm3533_bl_probe(struct platform_device * pdev)262 static int lm3533_bl_probe(struct platform_device *pdev)
263 {
264 	struct lm3533 *lm3533;
265 	struct lm3533_bl_platform_data *pdata;
266 	struct lm3533_bl *bl;
267 	struct backlight_device *bd;
268 	struct backlight_properties props;
269 	int ret;
270 
271 	dev_dbg(&pdev->dev, "%s\n", __func__);
272 
273 	lm3533 = dev_get_drvdata(pdev->dev.parent);
274 	if (!lm3533)
275 		return -EINVAL;
276 
277 	pdata = dev_get_platdata(&pdev->dev);
278 	if (!pdata) {
279 		dev_err(&pdev->dev, "no platform data\n");
280 		return -EINVAL;
281 	}
282 
283 	if (pdev->id < 0 || pdev->id >= LM3533_HVCTRLBANK_COUNT) {
284 		dev_err(&pdev->dev, "illegal backlight id %d\n", pdev->id);
285 		return -EINVAL;
286 	}
287 
288 	bl = devm_kzalloc(&pdev->dev, sizeof(*bl), GFP_KERNEL);
289 	if (!bl)
290 		return -ENOMEM;
291 
292 	bl->lm3533 = lm3533;
293 	bl->id = pdev->id;
294 
295 	bl->cb.lm3533 = lm3533;
296 	bl->cb.id = lm3533_bl_get_ctrlbank_id(bl);
297 	bl->cb.dev = NULL;			/* until registered */
298 
299 	memset(&props, 0, sizeof(props));
300 	props.type = BACKLIGHT_RAW;
301 	props.max_brightness = LM3533_BL_MAX_BRIGHTNESS;
302 	props.brightness = pdata->default_brightness;
303 	bd = devm_backlight_device_register(&pdev->dev, pdata->name,
304 					pdev->dev.parent, bl, &lm3533_bl_ops,
305 					&props);
306 	if (IS_ERR(bd)) {
307 		dev_err(&pdev->dev, "failed to register backlight device\n");
308 		return PTR_ERR(bd);
309 	}
310 
311 	bl->bd = bd;
312 	bl->cb.dev = &bl->bd->dev;
313 
314 	platform_set_drvdata(pdev, bl);
315 
316 	ret = sysfs_create_group(&bd->dev.kobj, &lm3533_bl_attribute_group);
317 	if (ret < 0) {
318 		dev_err(&pdev->dev, "failed to create sysfs attributes\n");
319 		return ret;
320 	}
321 
322 	backlight_update_status(bd);
323 
324 	ret = lm3533_bl_setup(bl, pdata);
325 	if (ret)
326 		goto err_sysfs_remove;
327 
328 	ret = lm3533_ctrlbank_enable(&bl->cb);
329 	if (ret)
330 		goto err_sysfs_remove;
331 
332 	return 0;
333 
334 err_sysfs_remove:
335 	sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group);
336 
337 	return ret;
338 }
339 
lm3533_bl_remove(struct platform_device * pdev)340 static int lm3533_bl_remove(struct platform_device *pdev)
341 {
342 	struct lm3533_bl *bl = platform_get_drvdata(pdev);
343 	struct backlight_device *bd = bl->bd;
344 
345 	dev_dbg(&bd->dev, "%s\n", __func__);
346 
347 	bd->props.power = FB_BLANK_POWERDOWN;
348 	bd->props.brightness = 0;
349 
350 	lm3533_ctrlbank_disable(&bl->cb);
351 	sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group);
352 
353 	return 0;
354 }
355 
356 #ifdef CONFIG_PM_SLEEP
lm3533_bl_suspend(struct device * dev)357 static int lm3533_bl_suspend(struct device *dev)
358 {
359 	struct lm3533_bl *bl = dev_get_drvdata(dev);
360 
361 	dev_dbg(dev, "%s\n", __func__);
362 
363 	return lm3533_ctrlbank_disable(&bl->cb);
364 }
365 
lm3533_bl_resume(struct device * dev)366 static int lm3533_bl_resume(struct device *dev)
367 {
368 	struct lm3533_bl *bl = dev_get_drvdata(dev);
369 
370 	dev_dbg(dev, "%s\n", __func__);
371 
372 	return lm3533_ctrlbank_enable(&bl->cb);
373 }
374 #endif
375 
376 static SIMPLE_DEV_PM_OPS(lm3533_bl_pm_ops, lm3533_bl_suspend, lm3533_bl_resume);
377 
lm3533_bl_shutdown(struct platform_device * pdev)378 static void lm3533_bl_shutdown(struct platform_device *pdev)
379 {
380 	struct lm3533_bl *bl = platform_get_drvdata(pdev);
381 
382 	dev_dbg(&pdev->dev, "%s\n", __func__);
383 
384 	lm3533_ctrlbank_disable(&bl->cb);
385 }
386 
387 static struct platform_driver lm3533_bl_driver = {
388 	.driver = {
389 		.name	= "lm3533-backlight",
390 		.pm	= &lm3533_bl_pm_ops,
391 	},
392 	.probe		= lm3533_bl_probe,
393 	.remove		= lm3533_bl_remove,
394 	.shutdown	= lm3533_bl_shutdown,
395 };
396 module_platform_driver(lm3533_bl_driver);
397 
398 MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>");
399 MODULE_DESCRIPTION("LM3533 Backlight driver");
400 MODULE_LICENSE("GPL");
401 MODULE_ALIAS("platform:lm3533-backlight");
402