]>
Commit | Line | Data |
---|---|---|
eb80411b KM |
1 | /* |
2 | * TI LP8788 MFD - keyled driver | |
3 | * | |
4 | * Copyright 2012 Texas Instruments | |
5 | * | |
6 | * Author: Milo(Woogyom) Kim <[email protected]> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | * | |
12 | */ | |
13 | ||
14 | #include <linux/module.h> | |
15 | #include <linux/slab.h> | |
16 | #include <linux/err.h> | |
17 | #include <linux/platform_device.h> | |
18 | #include <linux/leds.h> | |
19 | #include <linux/mutex.h> | |
20 | #include <linux/mfd/lp8788.h> | |
21 | #include <linux/mfd/lp8788-isink.h> | |
22 | ||
23 | #define MAX_BRIGHTNESS LP8788_ISINK_MAX_PWM | |
24 | #define DEFAULT_LED_NAME "keyboard-backlight" | |
25 | ||
26 | struct lp8788_led { | |
27 | struct lp8788 *lp; | |
28 | struct mutex lock; | |
eb80411b KM |
29 | struct led_classdev led_dev; |
30 | enum lp8788_isink_number isink_num; | |
eb80411b KM |
31 | int on; |
32 | }; | |
33 | ||
34 | struct lp8788_led_config { | |
35 | enum lp8788_isink_scale scale; | |
36 | enum lp8788_isink_number num; | |
37 | int iout; | |
38 | }; | |
39 | ||
40 | static struct lp8788_led_config default_led_config = { | |
41 | .scale = LP8788_ISINK_SCALE_100mA, | |
42 | .num = LP8788_ISINK_3, | |
43 | .iout = 0, | |
44 | }; | |
45 | ||
46 | static int lp8788_led_init_device(struct lp8788_led *led, | |
47 | struct lp8788_led_platform_data *pdata) | |
48 | { | |
49 | struct lp8788_led_config *cfg = &default_led_config; | |
50 | u8 addr, mask, val; | |
51 | int ret; | |
52 | ||
53 | if (pdata) { | |
54 | cfg->scale = pdata->scale; | |
55 | cfg->num = pdata->num; | |
56 | cfg->iout = pdata->iout_code; | |
57 | } | |
58 | ||
59 | led->isink_num = cfg->num; | |
60 | ||
61 | /* scale configuration */ | |
62 | addr = LP8788_ISINK_CTRL; | |
63 | mask = 1 << (cfg->num + LP8788_ISINK_SCALE_OFFSET); | |
4df7309a | 64 | val = cfg->scale << (cfg->num + LP8788_ISINK_SCALE_OFFSET); |
eb80411b KM |
65 | ret = lp8788_update_bits(led->lp, addr, mask, val); |
66 | if (ret) | |
67 | return ret; | |
68 | ||
69 | /* current configuration */ | |
70 | addr = lp8788_iout_addr[cfg->num]; | |
71 | mask = lp8788_iout_mask[cfg->num]; | |
72 | val = cfg->iout; | |
73 | ||
74 | return lp8788_update_bits(led->lp, addr, mask, val); | |
75 | } | |
76 | ||
64998371 | 77 | static int lp8788_led_enable(struct lp8788_led *led, |
eb80411b KM |
78 | enum lp8788_isink_number num, int on) |
79 | { | |
64998371 AL |
80 | int ret; |
81 | ||
eb80411b KM |
82 | u8 mask = 1 << num; |
83 | u8 val = on << num; | |
84 | ||
64998371 AL |
85 | ret = lp8788_update_bits(led->lp, LP8788_ISINK_CTRL, mask, val); |
86 | if (ret == 0) | |
87 | led->on = on; | |
eb80411b | 88 | |
64998371 | 89 | return ret; |
eb80411b KM |
90 | } |
91 | ||
64998371 AL |
92 | static int lp8788_brightness_set(struct led_classdev *led_cdev, |
93 | enum led_brightness val) | |
eb80411b | 94 | { |
64998371 AL |
95 | struct lp8788_led *led = |
96 | container_of(led_cdev, struct lp8788_led, led_dev); | |
97 | ||
eb80411b | 98 | enum lp8788_isink_number num = led->isink_num; |
64998371 | 99 | int enable, ret; |
eb80411b KM |
100 | |
101 | mutex_lock(&led->lock); | |
102 | ||
103 | switch (num) { | |
104 | case LP8788_ISINK_1: | |
105 | case LP8788_ISINK_2: | |
106 | case LP8788_ISINK_3: | |
64998371 AL |
107 | ret = lp8788_write_byte(led->lp, lp8788_pwm_addr[num], val); |
108 | if (ret < 0) | |
109 | goto unlock; | |
eb80411b KM |
110 | break; |
111 | default: | |
d45bb116 | 112 | mutex_unlock(&led->lock); |
64998371 | 113 | return -EINVAL; |
eb80411b KM |
114 | } |
115 | ||
116 | enable = (val > 0) ? 1 : 0; | |
117 | if (enable != led->on) | |
64998371 AL |
118 | ret = lp8788_led_enable(led, num, enable); |
119 | unlock: | |
eb80411b | 120 | mutex_unlock(&led->lock); |
64998371 | 121 | return ret; |
eb80411b KM |
122 | } |
123 | ||
98ea1ea2 | 124 | static int lp8788_led_probe(struct platform_device *pdev) |
eb80411b KM |
125 | { |
126 | struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); | |
127 | struct lp8788_led_platform_data *led_pdata; | |
128 | struct lp8788_led *led; | |
a1932edf | 129 | struct device *dev = &pdev->dev; |
eb80411b KM |
130 | int ret; |
131 | ||
a1932edf | 132 | led = devm_kzalloc(dev, sizeof(struct lp8788_led), GFP_KERNEL); |
eb80411b KM |
133 | if (!led) |
134 | return -ENOMEM; | |
135 | ||
136 | led->lp = lp; | |
137 | led->led_dev.max_brightness = MAX_BRIGHTNESS; | |
64998371 | 138 | led->led_dev.brightness_set_blocking = lp8788_brightness_set; |
eb80411b KM |
139 | |
140 | led_pdata = lp->pdata ? lp->pdata->led_pdata : NULL; | |
141 | ||
142 | if (!led_pdata || !led_pdata->name) | |
143 | led->led_dev.name = DEFAULT_LED_NAME; | |
144 | else | |
145 | led->led_dev.name = led_pdata->name; | |
146 | ||
147 | mutex_init(&led->lock); | |
eb80411b | 148 | |
eb80411b KM |
149 | ret = lp8788_led_init_device(led, led_pdata); |
150 | if (ret) { | |
a1932edf | 151 | dev_err(dev, "led init device err: %d\n", ret); |
eb80411b KM |
152 | return ret; |
153 | } | |
154 | ||
f165a66e | 155 | ret = devm_led_classdev_register(dev, &led->led_dev); |
eb80411b | 156 | if (ret) { |
a1932edf | 157 | dev_err(dev, "led register err: %d\n", ret); |
eb80411b KM |
158 | return ret; |
159 | } | |
160 | ||
161 | return 0; | |
162 | } | |
163 | ||
eb80411b KM |
164 | static struct platform_driver lp8788_led_driver = { |
165 | .probe = lp8788_led_probe, | |
eb80411b KM |
166 | .driver = { |
167 | .name = LP8788_DEV_KEYLED, | |
eb80411b KM |
168 | }, |
169 | }; | |
170 | module_platform_driver(lp8788_led_driver); | |
171 | ||
172 | MODULE_DESCRIPTION("Texas Instruments LP8788 Keyboard LED Driver"); | |
173 | MODULE_AUTHOR("Milo Kim"); | |
174 | MODULE_LICENSE("GPL"); | |
175 | MODULE_ALIAS("platform:lp8788-keyled"); |