1
2
3
4
5
6
7
8
9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10
11#include <linux/module.h>
12#include <linux/init.h>
13#include <linux/device.h>
14#include <linux/backlight.h>
15#include <linux/notifier.h>
16#include <linux/ctype.h>
17#include <linux/err.h>
18#include <linux/fb.h>
19#include <linux/slab.h>
20
21#ifdef CONFIG_PMAC_BACKLIGHT
22#include <asm/backlight.h>
23#endif
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66static struct list_head backlight_dev_list;
67static struct mutex backlight_dev_list_mutex;
68static struct blocking_notifier_head backlight_notifier;
69
70static const char *const backlight_types[] = {
71 [BACKLIGHT_RAW] = "raw",
72 [BACKLIGHT_PLATFORM] = "platform",
73 [BACKLIGHT_FIRMWARE] = "firmware",
74};
75
76static const char *const backlight_scale_types[] = {
77 [BACKLIGHT_SCALE_UNKNOWN] = "unknown",
78 [BACKLIGHT_SCALE_LINEAR] = "linear",
79 [BACKLIGHT_SCALE_NON_LINEAR] = "non-linear",
80};
81
82#if defined(CONFIG_FB) || (defined(CONFIG_FB_MODULE) && \
83 defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE))
84
85
86
87
88
89
90
91
92
93
94
95
96static int fb_notifier_callback(struct notifier_block *self,
97 unsigned long event, void *data)
98{
99 struct backlight_device *bd;
100 struct fb_event *evdata = data;
101 int node = evdata->info->node;
102 int fb_blank = 0;
103
104
105 if (event != FB_EVENT_BLANK)
106 return 0;
107
108 bd = container_of(self, struct backlight_device, fb_notif);
109 mutex_lock(&bd->ops_lock);
110
111 if (!bd->ops)
112 goto out;
113 if (bd->ops->check_fb && !bd->ops->check_fb(bd, evdata->info))
114 goto out;
115
116 fb_blank = *(int *)evdata->data;
117 if (fb_blank == FB_BLANK_UNBLANK && !bd->fb_bl_on[node]) {
118 bd->fb_bl_on[node] = true;
119 if (!bd->use_count++) {
120 bd->props.state &= ~BL_CORE_FBBLANK;
121 bd->props.fb_blank = FB_BLANK_UNBLANK;
122 backlight_update_status(bd);
123 }
124 } else if (fb_blank != FB_BLANK_UNBLANK && bd->fb_bl_on[node]) {
125 bd->fb_bl_on[node] = false;
126 if (!(--bd->use_count)) {
127 bd->props.state |= BL_CORE_FBBLANK;
128 bd->props.fb_blank = fb_blank;
129 backlight_update_status(bd);
130 }
131 }
132out:
133 mutex_unlock(&bd->ops_lock);
134 return 0;
135}
136
137static int backlight_register_fb(struct backlight_device *bd)
138{
139 memset(&bd->fb_notif, 0, sizeof(bd->fb_notif));
140 bd->fb_notif.notifier_call = fb_notifier_callback;
141
142 return fb_register_client(&bd->fb_notif);
143}
144
145static void backlight_unregister_fb(struct backlight_device *bd)
146{
147 fb_unregister_client(&bd->fb_notif);
148}
149#else
150static inline int backlight_register_fb(struct backlight_device *bd)
151{
152 return 0;
153}
154
155static inline void backlight_unregister_fb(struct backlight_device *bd)
156{
157}
158#endif
159
160static void backlight_generate_event(struct backlight_device *bd,
161 enum backlight_update_reason reason)
162{
163 char *envp[2];
164
165 switch (reason) {
166 case BACKLIGHT_UPDATE_SYSFS:
167 envp[0] = "SOURCE=sysfs";
168 break;
169 case BACKLIGHT_UPDATE_HOTKEY:
170 envp[0] = "SOURCE=hotkey";
171 break;
172 default:
173 envp[0] = "SOURCE=unknown";
174 break;
175 }
176 envp[1] = NULL;
177 kobject_uevent_env(&bd->dev.kobj, KOBJ_CHANGE, envp);
178 sysfs_notify(&bd->dev.kobj, NULL, "actual_brightness");
179}
180
181static ssize_t bl_power_show(struct device *dev, struct device_attribute *attr,
182 char *buf)
183{
184 struct backlight_device *bd = to_backlight_device(dev);
185
186 return sprintf(buf, "%d\n", bd->props.power);
187}
188
189static ssize_t bl_power_store(struct device *dev, struct device_attribute *attr,
190 const char *buf, size_t count)
191{
192 int rc;
193 struct backlight_device *bd = to_backlight_device(dev);
194 unsigned long power, old_power;
195
196 rc = kstrtoul(buf, 0, &power);
197 if (rc)
198 return rc;
199
200 rc = -ENXIO;
201 mutex_lock(&bd->ops_lock);
202 if (bd->ops) {
203 pr_debug("set power to %lu\n", power);
204 if (bd->props.power != power) {
205 old_power = bd->props.power;
206 bd->props.power = power;
207 rc = backlight_update_status(bd);
208 if (rc)
209 bd->props.power = old_power;
210 else
211 rc = count;
212 } else {
213 rc = count;
214 }
215 }
216 mutex_unlock(&bd->ops_lock);
217
218 return rc;
219}
220static DEVICE_ATTR_RW(bl_power);
221
222static ssize_t brightness_show(struct device *dev,
223 struct device_attribute *attr, char *buf)
224{
225 struct backlight_device *bd = to_backlight_device(dev);
226
227 return sprintf(buf, "%d\n", bd->props.brightness);
228}
229
230int backlight_device_set_brightness(struct backlight_device *bd,
231 unsigned long brightness)
232{
233 int rc = -ENXIO;
234
235 mutex_lock(&bd->ops_lock);
236 if (bd->ops) {
237 if (brightness > bd->props.max_brightness)
238 rc = -EINVAL;
239 else {
240 pr_debug("set brightness to %lu\n", brightness);
241 bd->props.brightness = brightness;
242 rc = backlight_update_status(bd);
243 }
244 }
245 mutex_unlock(&bd->ops_lock);
246
247 backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS);
248
249 return rc;
250}
251EXPORT_SYMBOL(backlight_device_set_brightness);
252
253static ssize_t brightness_store(struct device *dev,
254 struct device_attribute *attr, const char *buf, size_t count)
255{
256 int rc;
257 struct backlight_device *bd = to_backlight_device(dev);
258 unsigned long brightness;
259
260 rc = kstrtoul(buf, 0, &brightness);
261 if (rc)
262 return rc;
263
264 rc = backlight_device_set_brightness(bd, brightness);
265
266 return rc ? rc : count;
267}
268static DEVICE_ATTR_RW(brightness);
269
270static ssize_t type_show(struct device *dev, struct device_attribute *attr,
271 char *buf)
272{
273 struct backlight_device *bd = to_backlight_device(dev);
274
275 return sprintf(buf, "%s\n", backlight_types[bd->props.type]);
276}
277static DEVICE_ATTR_RO(type);
278
279static ssize_t max_brightness_show(struct device *dev,
280 struct device_attribute *attr, char *buf)
281{
282 struct backlight_device *bd = to_backlight_device(dev);
283
284 return sprintf(buf, "%d\n", bd->props.max_brightness);
285}
286static DEVICE_ATTR_RO(max_brightness);
287
288static ssize_t actual_brightness_show(struct device *dev,
289 struct device_attribute *attr, char *buf)
290{
291 int rc = -ENXIO;
292 struct backlight_device *bd = to_backlight_device(dev);
293
294 mutex_lock(&bd->ops_lock);
295 if (bd->ops && bd->ops->get_brightness)
296 rc = sprintf(buf, "%d\n", bd->ops->get_brightness(bd));
297 else
298 rc = sprintf(buf, "%d\n", bd->props.brightness);
299 mutex_unlock(&bd->ops_lock);
300
301 return rc;
302}
303static DEVICE_ATTR_RO(actual_brightness);
304
305static ssize_t scale_show(struct device *dev,
306 struct device_attribute *attr, char *buf)
307{
308 struct backlight_device *bd = to_backlight_device(dev);
309
310 if (WARN_ON(bd->props.scale > BACKLIGHT_SCALE_NON_LINEAR))
311 return sprintf(buf, "unknown\n");
312
313 return sprintf(buf, "%s\n", backlight_scale_types[bd->props.scale]);
314}
315static DEVICE_ATTR_RO(scale);
316
317static struct class *backlight_class;
318
319#ifdef CONFIG_PM_SLEEP
320static int backlight_suspend(struct device *dev)
321{
322 struct backlight_device *bd = to_backlight_device(dev);
323
324 mutex_lock(&bd->ops_lock);
325 if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) {
326 bd->props.state |= BL_CORE_SUSPENDED;
327 backlight_update_status(bd);
328 }
329 mutex_unlock(&bd->ops_lock);
330
331 return 0;
332}
333
334static int backlight_resume(struct device *dev)
335{
336 struct backlight_device *bd = to_backlight_device(dev);
337
338 mutex_lock(&bd->ops_lock);
339 if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) {
340 bd->props.state &= ~BL_CORE_SUSPENDED;
341 backlight_update_status(bd);
342 }
343 mutex_unlock(&bd->ops_lock);
344
345 return 0;
346}
347#endif
348
349static SIMPLE_DEV_PM_OPS(backlight_class_dev_pm_ops, backlight_suspend,
350 backlight_resume);
351
352static void bl_device_release(struct device *dev)
353{
354 struct backlight_device *bd = to_backlight_device(dev);
355 kfree(bd);
356}
357
358static struct attribute *bl_device_attrs[] = {
359 &dev_attr_bl_power.attr,
360 &dev_attr_brightness.attr,
361 &dev_attr_actual_brightness.attr,
362 &dev_attr_max_brightness.attr,
363 &dev_attr_scale.attr,
364 &dev_attr_type.attr,
365 NULL,
366};
367ATTRIBUTE_GROUPS(bl_device);
368
369
370
371
372
373
374
375
376
377
378
379
380
381void backlight_force_update(struct backlight_device *bd,
382 enum backlight_update_reason reason)
383{
384 mutex_lock(&bd->ops_lock);
385 if (bd->ops && bd->ops->get_brightness)
386 bd->props.brightness = bd->ops->get_brightness(bd);
387 mutex_unlock(&bd->ops_lock);
388 backlight_generate_event(bd, reason);
389}
390EXPORT_SYMBOL(backlight_force_update);
391
392
393struct backlight_device *backlight_device_register(const char *name,
394 struct device *parent, void *devdata, const struct backlight_ops *ops,
395 const struct backlight_properties *props)
396{
397 struct backlight_device *new_bd;
398 int rc;
399
400 pr_debug("backlight_device_register: name=%s\n", name);
401
402 new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL);
403 if (!new_bd)
404 return ERR_PTR(-ENOMEM);
405
406 mutex_init(&new_bd->update_lock);
407 mutex_init(&new_bd->ops_lock);
408
409 new_bd->dev.class = backlight_class;
410 new_bd->dev.parent = parent;
411 new_bd->dev.release = bl_device_release;
412 dev_set_name(&new_bd->dev, "%s", name);
413 dev_set_drvdata(&new_bd->dev, devdata);
414
415
416 if (props) {
417 memcpy(&new_bd->props, props,
418 sizeof(struct backlight_properties));
419 if (props->type <= 0 || props->type >= BACKLIGHT_TYPE_MAX) {
420 WARN(1, "%s: invalid backlight type", name);
421 new_bd->props.type = BACKLIGHT_RAW;
422 }
423 } else {
424 new_bd->props.type = BACKLIGHT_RAW;
425 }
426
427 rc = device_register(&new_bd->dev);
428 if (rc) {
429 put_device(&new_bd->dev);
430 return ERR_PTR(rc);
431 }
432
433 rc = backlight_register_fb(new_bd);
434 if (rc) {
435 device_unregister(&new_bd->dev);
436 return ERR_PTR(rc);
437 }
438
439 new_bd->ops = ops;
440
441#ifdef CONFIG_PMAC_BACKLIGHT
442 mutex_lock(&pmac_backlight_mutex);
443 if (!pmac_backlight)
444 pmac_backlight = new_bd;
445 mutex_unlock(&pmac_backlight_mutex);
446#endif
447
448 mutex_lock(&backlight_dev_list_mutex);
449 list_add(&new_bd->entry, &backlight_dev_list);
450 mutex_unlock(&backlight_dev_list_mutex);
451
452 blocking_notifier_call_chain(&backlight_notifier,
453 BACKLIGHT_REGISTERED, new_bd);
454
455 return new_bd;
456}
457EXPORT_SYMBOL(backlight_device_register);
458
459
460
461
462
463
464
465
466
467
468struct backlight_device *backlight_device_get_by_type(enum backlight_type type)
469{
470 bool found = false;
471 struct backlight_device *bd;
472
473 mutex_lock(&backlight_dev_list_mutex);
474 list_for_each_entry(bd, &backlight_dev_list, entry) {
475 if (bd->props.type == type) {
476 found = true;
477 break;
478 }
479 }
480 mutex_unlock(&backlight_dev_list_mutex);
481
482 return found ? bd : NULL;
483}
484EXPORT_SYMBOL(backlight_device_get_by_type);
485
486
487
488
489
490
491
492
493
494
495
496
497struct backlight_device *backlight_device_get_by_name(const char *name)
498{
499 struct device *dev;
500
501 dev = class_find_device_by_name(backlight_class, name);
502
503 return dev ? to_backlight_device(dev) : NULL;
504}
505EXPORT_SYMBOL(backlight_device_get_by_name);
506
507
508void backlight_device_unregister(struct backlight_device *bd)
509{
510 if (!bd)
511 return;
512
513 mutex_lock(&backlight_dev_list_mutex);
514 list_del(&bd->entry);
515 mutex_unlock(&backlight_dev_list_mutex);
516
517#ifdef CONFIG_PMAC_BACKLIGHT
518 mutex_lock(&pmac_backlight_mutex);
519 if (pmac_backlight == bd)
520 pmac_backlight = NULL;
521 mutex_unlock(&pmac_backlight_mutex);
522#endif
523
524 blocking_notifier_call_chain(&backlight_notifier,
525 BACKLIGHT_UNREGISTERED, bd);
526
527 mutex_lock(&bd->ops_lock);
528 bd->ops = NULL;
529 mutex_unlock(&bd->ops_lock);
530
531 backlight_unregister_fb(bd);
532 device_unregister(&bd->dev);
533}
534EXPORT_SYMBOL(backlight_device_unregister);
535
536static void devm_backlight_device_release(struct device *dev, void *res)
537{
538 struct backlight_device *backlight = *(struct backlight_device **)res;
539
540 backlight_device_unregister(backlight);
541}
542
543static int devm_backlight_device_match(struct device *dev, void *res,
544 void *data)
545{
546 struct backlight_device **r = res;
547
548 return *r == data;
549}
550
551
552
553
554
555
556
557
558
559
560
561
562int backlight_register_notifier(struct notifier_block *nb)
563{
564 return blocking_notifier_chain_register(&backlight_notifier, nb);
565}
566EXPORT_SYMBOL(backlight_register_notifier);
567
568
569
570
571
572
573
574
575
576
577
578
579int backlight_unregister_notifier(struct notifier_block *nb)
580{
581 return blocking_notifier_chain_unregister(&backlight_notifier, nb);
582}
583EXPORT_SYMBOL(backlight_unregister_notifier);
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602struct backlight_device *devm_backlight_device_register(struct device *dev,
603 const char *name, struct device *parent, void *devdata,
604 const struct backlight_ops *ops,
605 const struct backlight_properties *props)
606{
607 struct backlight_device **ptr, *backlight;
608
609 ptr = devres_alloc(devm_backlight_device_release, sizeof(*ptr),
610 GFP_KERNEL);
611 if (!ptr)
612 return ERR_PTR(-ENOMEM);
613
614 backlight = backlight_device_register(name, parent, devdata, ops,
615 props);
616 if (!IS_ERR(backlight)) {
617 *ptr = backlight;
618 devres_add(dev, ptr);
619 } else {
620 devres_free(ptr);
621 }
622
623 return backlight;
624}
625EXPORT_SYMBOL(devm_backlight_device_register);
626
627
628
629
630
631
632
633
634
635
636void devm_backlight_device_unregister(struct device *dev,
637 struct backlight_device *bd)
638{
639 int rc;
640
641 rc = devres_release(dev, devm_backlight_device_release,
642 devm_backlight_device_match, bd);
643 WARN_ON(rc);
644}
645EXPORT_SYMBOL(devm_backlight_device_unregister);
646
647#ifdef CONFIG_OF
648static int of_parent_match(struct device *dev, const void *data)
649{
650 return dev->parent && dev->parent->of_node == data;
651}
652
653
654
655
656
657
658
659
660
661
662
663
664
665struct backlight_device *of_find_backlight_by_node(struct device_node *node)
666{
667 struct device *dev;
668
669 dev = class_find_device(backlight_class, NULL, node, of_parent_match);
670
671 return dev ? to_backlight_device(dev) : NULL;
672}
673EXPORT_SYMBOL(of_find_backlight_by_node);
674#endif
675
676static struct backlight_device *of_find_backlight(struct device *dev)
677{
678 struct backlight_device *bd = NULL;
679 struct device_node *np;
680
681 if (!dev)
682 return NULL;
683
684 if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
685 np = of_parse_phandle(dev->of_node, "backlight", 0);
686 if (np) {
687 bd = of_find_backlight_by_node(np);
688 of_node_put(np);
689 if (!bd)
690 return ERR_PTR(-EPROBE_DEFER);
691
692
693
694
695 if (!bd->props.brightness)
696 bd->props.brightness = bd->props.max_brightness;
697 }
698 }
699
700 return bd;
701}
702
703static void devm_backlight_release(void *data)
704{
705 struct backlight_device *bd = data;
706
707 if (bd)
708 put_device(&bd->dev);
709}
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726struct backlight_device *devm_of_find_backlight(struct device *dev)
727{
728 struct backlight_device *bd;
729 int ret;
730
731 bd = of_find_backlight(dev);
732 if (IS_ERR_OR_NULL(bd))
733 return bd;
734 ret = devm_add_action(dev, devm_backlight_release, bd);
735 if (ret) {
736 put_device(&bd->dev);
737 return ERR_PTR(ret);
738 }
739 return bd;
740}
741EXPORT_SYMBOL(devm_of_find_backlight);
742
743static void __exit backlight_class_exit(void)
744{
745 class_destroy(backlight_class);
746}
747
748static int __init backlight_class_init(void)
749{
750 backlight_class = class_create(THIS_MODULE, "backlight");
751 if (IS_ERR(backlight_class)) {
752 pr_warn("Unable to create backlight class; errno = %ld\n",
753 PTR_ERR(backlight_class));
754 return PTR_ERR(backlight_class);
755 }
756
757 backlight_class->dev_groups = bl_device_groups;
758 backlight_class->pm = &backlight_class_dev_pm_ops;
759 INIT_LIST_HEAD(&backlight_dev_list);
760 mutex_init(&backlight_dev_list_mutex);
761 BLOCKING_INIT_NOTIFIER_HEAD(&backlight_notifier);
762
763 return 0;
764}
765
766
767
768
769
770postcore_initcall(backlight_class_init);
771module_exit(backlight_class_exit);
772
773MODULE_LICENSE("GPL");
774MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>");
775MODULE_DESCRIPTION("Backlight Lowlevel Control Abstraction");
776