1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Datasheet: |
4 | * https://www.kinet-ic.com/uploads/web/KTD2801/KTD2801-04b.pdf |
5 | */ |
6 | #include <linux/backlight.h> |
7 | #include <linux/gpio/consumer.h> |
8 | #include <linux/leds-expresswire.h> |
9 | #include <linux/platform_device.h> |
10 | #include <linux/property.h> |
11 | |
12 | #define KTD2801_DEFAULT_BRIGHTNESS 100 |
13 | #define KTD2801_MAX_BRIGHTNESS 255 |
14 | |
15 | /* These values have been extracted from Samsung's driver. */ |
16 | static const struct expresswire_timing ktd2801_timing = { |
17 | .poweroff_us = 2600, |
18 | .detect_delay_us = 150, |
19 | .detect_us = 270, |
20 | .data_start_us = 5, |
21 | .short_bitset_us = 5, |
22 | .long_bitset_us = 15, |
23 | .end_of_data_low_us = 10, |
24 | .end_of_data_high_us = 350 |
25 | }; |
26 | |
27 | struct ktd2801_backlight { |
28 | struct expresswire_common_props props; |
29 | struct backlight_device *bd; |
30 | bool was_on; |
31 | }; |
32 | |
33 | static int ktd2801_update_status(struct backlight_device *bd) |
34 | { |
35 | struct ktd2801_backlight *ktd2801 = bl_get_data(bl_dev: bd); |
36 | u8 brightness = (u8) backlight_get_brightness(bd); |
37 | |
38 | if (backlight_is_blank(bd)) { |
39 | expresswire_power_off(props: &ktd2801->props); |
40 | ktd2801->was_on = false; |
41 | return 0; |
42 | } |
43 | |
44 | if (!ktd2801->was_on) { |
45 | expresswire_enable(props: &ktd2801->props); |
46 | ktd2801->was_on = true; |
47 | } |
48 | |
49 | expresswire_write_u8(props: &ktd2801->props, val: brightness); |
50 | |
51 | return 0; |
52 | } |
53 | |
54 | static const struct backlight_ops ktd2801_backlight_ops = { |
55 | .update_status = ktd2801_update_status, |
56 | }; |
57 | |
58 | static int ktd2801_backlight_probe(struct platform_device *pdev) |
59 | { |
60 | struct device *dev = &pdev->dev; |
61 | struct backlight_device *bd; |
62 | struct ktd2801_backlight *ktd2801; |
63 | u32 brightness, max_brightness; |
64 | int ret; |
65 | |
66 | ktd2801 = devm_kzalloc(dev, size: sizeof(*ktd2801), GFP_KERNEL); |
67 | if (!ktd2801) |
68 | return -ENOMEM; |
69 | ktd2801->was_on = true; |
70 | ktd2801->props.timing = ktd2801_timing; |
71 | |
72 | ret = device_property_read_u32(dev, propname: "max-brightness" , val: &max_brightness); |
73 | if (ret) |
74 | max_brightness = KTD2801_MAX_BRIGHTNESS; |
75 | if (max_brightness > KTD2801_MAX_BRIGHTNESS) { |
76 | dev_err(dev, "illegal max brightness specified\n" ); |
77 | max_brightness = KTD2801_MAX_BRIGHTNESS; |
78 | } |
79 | |
80 | ret = device_property_read_u32(dev, propname: "default-brightness" , val: &brightness); |
81 | if (ret) |
82 | brightness = KTD2801_DEFAULT_BRIGHTNESS; |
83 | if (brightness > max_brightness) { |
84 | dev_err(dev, "default brightness exceeds max\n" ); |
85 | brightness = max_brightness; |
86 | } |
87 | |
88 | ktd2801->props.ctrl_gpio = devm_gpiod_get(dev, con_id: "ctrl" , flags: GPIOD_OUT_HIGH); |
89 | if (IS_ERR(ptr: ktd2801->props.ctrl_gpio)) |
90 | return dev_err_probe(dev, err: PTR_ERR(ptr: ktd2801->props.ctrl_gpio), |
91 | fmt: "failed to get backlight GPIO" ); |
92 | gpiod_set_consumer_name(desc: ktd2801->props.ctrl_gpio, name: dev_name(dev)); |
93 | |
94 | bd = devm_backlight_device_register(dev, name: dev_name(dev), parent: dev, devdata: ktd2801, |
95 | ops: &ktd2801_backlight_ops, NULL); |
96 | if (IS_ERR(ptr: bd)) |
97 | return dev_err_probe(dev, err: PTR_ERR(ptr: bd), |
98 | fmt: "failed to register backlight" ); |
99 | |
100 | bd->props.max_brightness = max_brightness; |
101 | bd->props.brightness = brightness; |
102 | |
103 | ktd2801->bd = bd; |
104 | platform_set_drvdata(pdev, data: bd); |
105 | backlight_update_status(bd); |
106 | |
107 | return 0; |
108 | } |
109 | |
110 | static const struct of_device_id ktd2801_of_match[] = { |
111 | { .compatible = "kinetic,ktd2801" }, |
112 | { } |
113 | }; |
114 | MODULE_DEVICE_TABLE(of, ktd2801_of_match); |
115 | |
116 | static struct platform_driver ktd2801_backlight_driver = { |
117 | .driver = { |
118 | .name = "ktd2801-backlight" , |
119 | .of_match_table = ktd2801_of_match, |
120 | }, |
121 | .probe = ktd2801_backlight_probe, |
122 | }; |
123 | module_platform_driver(ktd2801_backlight_driver); |
124 | |
125 | MODULE_IMPORT_NS(EXPRESSWIRE); |
126 | MODULE_AUTHOR("Duje Mihanović <duje.mihanovic@skole.hr>" ); |
127 | MODULE_DESCRIPTION("Kinetic KTD2801 Backlight Driver" ); |
128 | MODULE_LICENSE("GPL" ); |
129 | |