Root/
1 | /* |
2 | * leds-max8997.c - LED class driver for MAX8997 LEDs. |
3 | * |
4 | * Copyright (C) 2011 Samsung Electronics |
5 | * Donggeun Kim <dg77.kim@samsung.com> |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2 as |
9 | * published by the Free Software Foundation. |
10 | * |
11 | */ |
12 | |
13 | #include <linux/module.h> |
14 | #include <linux/err.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/workqueue.h> |
17 | #include <linux/leds.h> |
18 | #include <linux/mfd/max8997.h> |
19 | #include <linux/mfd/max8997-private.h> |
20 | #include <linux/platform_device.h> |
21 | |
22 | #define MAX8997_LED_FLASH_SHIFT 3 |
23 | #define MAX8997_LED_FLASH_CUR_MASK 0xf8 |
24 | #define MAX8997_LED_MOVIE_SHIFT 4 |
25 | #define MAX8997_LED_MOVIE_CUR_MASK 0xf0 |
26 | |
27 | #define MAX8997_LED_FLASH_MAX_BRIGHTNESS 0x1f |
28 | #define MAX8997_LED_MOVIE_MAX_BRIGHTNESS 0xf |
29 | #define MAX8997_LED_NONE_MAX_BRIGHTNESS 0 |
30 | |
31 | #define MAX8997_LED0_FLASH_MASK 0x1 |
32 | #define MAX8997_LED0_FLASH_PIN_MASK 0x5 |
33 | #define MAX8997_LED0_MOVIE_MASK 0x8 |
34 | #define MAX8997_LED0_MOVIE_PIN_MASK 0x28 |
35 | |
36 | #define MAX8997_LED1_FLASH_MASK 0x2 |
37 | #define MAX8997_LED1_FLASH_PIN_MASK 0x6 |
38 | #define MAX8997_LED1_MOVIE_MASK 0x10 |
39 | #define MAX8997_LED1_MOVIE_PIN_MASK 0x30 |
40 | |
41 | #define MAX8997_LED_BOOST_ENABLE_MASK (1 << 6) |
42 | |
43 | struct max8997_led { |
44 | struct max8997_dev *iodev; |
45 | struct led_classdev cdev; |
46 | bool enabled; |
47 | int id; |
48 | enum max8997_led_mode led_mode; |
49 | struct mutex mutex; |
50 | }; |
51 | |
52 | static void max8997_led_set_mode(struct max8997_led *led, |
53 | enum max8997_led_mode mode) |
54 | { |
55 | int ret; |
56 | struct i2c_client *client = led->iodev->i2c; |
57 | u8 mask = 0, val; |
58 | |
59 | switch (mode) { |
60 | case MAX8997_FLASH_MODE: |
61 | mask = MAX8997_LED1_FLASH_MASK | MAX8997_LED0_FLASH_MASK; |
62 | val = led->id ? |
63 | MAX8997_LED1_FLASH_MASK : MAX8997_LED0_FLASH_MASK; |
64 | led->cdev.max_brightness = MAX8997_LED_FLASH_MAX_BRIGHTNESS; |
65 | break; |
66 | case MAX8997_MOVIE_MODE: |
67 | mask = MAX8997_LED1_MOVIE_MASK | MAX8997_LED0_MOVIE_MASK; |
68 | val = led->id ? |
69 | MAX8997_LED1_MOVIE_MASK : MAX8997_LED0_MOVIE_MASK; |
70 | led->cdev.max_brightness = MAX8997_LED_MOVIE_MAX_BRIGHTNESS; |
71 | break; |
72 | case MAX8997_FLASH_PIN_CONTROL_MODE: |
73 | mask = MAX8997_LED1_FLASH_PIN_MASK | |
74 | MAX8997_LED0_FLASH_PIN_MASK; |
75 | val = led->id ? |
76 | MAX8997_LED1_FLASH_PIN_MASK : MAX8997_LED0_FLASH_PIN_MASK; |
77 | led->cdev.max_brightness = MAX8997_LED_FLASH_MAX_BRIGHTNESS; |
78 | break; |
79 | case MAX8997_MOVIE_PIN_CONTROL_MODE: |
80 | mask = MAX8997_LED1_MOVIE_PIN_MASK | |
81 | MAX8997_LED0_MOVIE_PIN_MASK; |
82 | val = led->id ? |
83 | MAX8997_LED1_MOVIE_PIN_MASK : MAX8997_LED0_MOVIE_PIN_MASK; |
84 | led->cdev.max_brightness = MAX8997_LED_MOVIE_MAX_BRIGHTNESS; |
85 | break; |
86 | default: |
87 | led->cdev.max_brightness = MAX8997_LED_NONE_MAX_BRIGHTNESS; |
88 | break; |
89 | } |
90 | |
91 | if (mask) { |
92 | ret = max8997_update_reg(client, MAX8997_REG_LEN_CNTL, val, |
93 | mask); |
94 | if (ret) |
95 | dev_err(led->iodev->dev, |
96 | "failed to update register(%d)\n", ret); |
97 | } |
98 | |
99 | led->led_mode = mode; |
100 | } |
101 | |
102 | static void max8997_led_enable(struct max8997_led *led, bool enable) |
103 | { |
104 | int ret; |
105 | struct i2c_client *client = led->iodev->i2c; |
106 | u8 val = 0, mask = MAX8997_LED_BOOST_ENABLE_MASK; |
107 | |
108 | if (led->enabled == enable) |
109 | return; |
110 | |
111 | val = enable ? MAX8997_LED_BOOST_ENABLE_MASK : 0; |
112 | |
113 | ret = max8997_update_reg(client, MAX8997_REG_BOOST_CNTL, val, mask); |
114 | if (ret) |
115 | dev_err(led->iodev->dev, |
116 | "failed to update register(%d)\n", ret); |
117 | |
118 | led->enabled = enable; |
119 | } |
120 | |
121 | static void max8997_led_set_current(struct max8997_led *led, |
122 | enum led_brightness value) |
123 | { |
124 | int ret; |
125 | struct i2c_client *client = led->iodev->i2c; |
126 | u8 val = 0, mask = 0, reg = 0; |
127 | |
128 | switch (led->led_mode) { |
129 | case MAX8997_FLASH_MODE: |
130 | case MAX8997_FLASH_PIN_CONTROL_MODE: |
131 | val = value << MAX8997_LED_FLASH_SHIFT; |
132 | mask = MAX8997_LED_FLASH_CUR_MASK; |
133 | reg = led->id ? MAX8997_REG_FLASH2_CUR : MAX8997_REG_FLASH1_CUR; |
134 | break; |
135 | case MAX8997_MOVIE_MODE: |
136 | case MAX8997_MOVIE_PIN_CONTROL_MODE: |
137 | val = value << MAX8997_LED_MOVIE_SHIFT; |
138 | mask = MAX8997_LED_MOVIE_CUR_MASK; |
139 | reg = MAX8997_REG_MOVIE_CUR; |
140 | break; |
141 | default: |
142 | break; |
143 | } |
144 | |
145 | if (mask) { |
146 | ret = max8997_update_reg(client, reg, val, mask); |
147 | if (ret) |
148 | dev_err(led->iodev->dev, |
149 | "failed to update register(%d)\n", ret); |
150 | } |
151 | } |
152 | |
153 | static void max8997_led_brightness_set(struct led_classdev *led_cdev, |
154 | enum led_brightness value) |
155 | { |
156 | struct max8997_led *led = |
157 | container_of(led_cdev, struct max8997_led, cdev); |
158 | |
159 | if (value) { |
160 | max8997_led_set_current(led, value); |
161 | max8997_led_enable(led, true); |
162 | } else { |
163 | max8997_led_set_current(led, value); |
164 | max8997_led_enable(led, false); |
165 | } |
166 | } |
167 | |
168 | static ssize_t max8997_led_show_mode(struct device *dev, |
169 | struct device_attribute *attr, char *buf) |
170 | { |
171 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
172 | struct max8997_led *led = |
173 | container_of(led_cdev, struct max8997_led, cdev); |
174 | ssize_t ret = 0; |
175 | |
176 | mutex_lock(&led->mutex); |
177 | |
178 | switch (led->led_mode) { |
179 | case MAX8997_FLASH_MODE: |
180 | ret += sprintf(buf, "FLASH\n"); |
181 | break; |
182 | case MAX8997_MOVIE_MODE: |
183 | ret += sprintf(buf, "MOVIE\n"); |
184 | break; |
185 | case MAX8997_FLASH_PIN_CONTROL_MODE: |
186 | ret += sprintf(buf, "FLASH_PIN_CONTROL\n"); |
187 | break; |
188 | case MAX8997_MOVIE_PIN_CONTROL_MODE: |
189 | ret += sprintf(buf, "MOVIE_PIN_CONTROL\n"); |
190 | break; |
191 | default: |
192 | ret += sprintf(buf, "NONE\n"); |
193 | break; |
194 | } |
195 | |
196 | mutex_unlock(&led->mutex); |
197 | |
198 | return ret; |
199 | } |
200 | |
201 | static ssize_t max8997_led_store_mode(struct device *dev, |
202 | struct device_attribute *attr, |
203 | const char *buf, size_t size) |
204 | { |
205 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
206 | struct max8997_led *led = |
207 | container_of(led_cdev, struct max8997_led, cdev); |
208 | enum max8997_led_mode mode; |
209 | |
210 | mutex_lock(&led->mutex); |
211 | |
212 | if (!strncmp(buf, "FLASH_PIN_CONTROL", 17)) |
213 | mode = MAX8997_FLASH_PIN_CONTROL_MODE; |
214 | else if (!strncmp(buf, "MOVIE_PIN_CONTROL", 17)) |
215 | mode = MAX8997_MOVIE_PIN_CONTROL_MODE; |
216 | else if (!strncmp(buf, "FLASH", 5)) |
217 | mode = MAX8997_FLASH_MODE; |
218 | else if (!strncmp(buf, "MOVIE", 5)) |
219 | mode = MAX8997_MOVIE_MODE; |
220 | else |
221 | mode = MAX8997_NONE; |
222 | |
223 | max8997_led_set_mode(led, mode); |
224 | |
225 | mutex_unlock(&led->mutex); |
226 | |
227 | return size; |
228 | } |
229 | |
230 | static DEVICE_ATTR(mode, 0644, max8997_led_show_mode, max8997_led_store_mode); |
231 | |
232 | static int __devinit max8997_led_probe(struct platform_device *pdev) |
233 | { |
234 | struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); |
235 | struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev); |
236 | struct max8997_led *led; |
237 | char name[20]; |
238 | int ret = 0; |
239 | |
240 | if (pdata == NULL) { |
241 | dev_err(&pdev->dev, "no platform data\n"); |
242 | return -ENODEV; |
243 | } |
244 | |
245 | led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); |
246 | if (led == NULL) |
247 | return -ENOMEM; |
248 | |
249 | led->id = pdev->id; |
250 | snprintf(name, sizeof(name), "max8997-led%d", pdev->id); |
251 | |
252 | led->cdev.name = name; |
253 | led->cdev.brightness_set = max8997_led_brightness_set; |
254 | led->cdev.flags |= LED_CORE_SUSPENDRESUME; |
255 | led->cdev.brightness = 0; |
256 | led->iodev = iodev; |
257 | |
258 | /* initialize mode and brightness according to platform_data */ |
259 | if (pdata->led_pdata) { |
260 | u8 mode = 0, brightness = 0; |
261 | |
262 | mode = pdata->led_pdata->mode[led->id]; |
263 | brightness = pdata->led_pdata->brightness[led->id]; |
264 | |
265 | max8997_led_set_mode(led, pdata->led_pdata->mode[led->id]); |
266 | |
267 | if (brightness > led->cdev.max_brightness) |
268 | brightness = led->cdev.max_brightness; |
269 | max8997_led_set_current(led, brightness); |
270 | led->cdev.brightness = brightness; |
271 | } else { |
272 | max8997_led_set_mode(led, MAX8997_NONE); |
273 | max8997_led_set_current(led, 0); |
274 | } |
275 | |
276 | mutex_init(&led->mutex); |
277 | |
278 | platform_set_drvdata(pdev, led); |
279 | |
280 | ret = led_classdev_register(&pdev->dev, &led->cdev); |
281 | if (ret < 0) |
282 | return ret; |
283 | |
284 | ret = device_create_file(led->cdev.dev, &dev_attr_mode); |
285 | if (ret != 0) { |
286 | dev_err(&pdev->dev, |
287 | "failed to create file: %d\n", ret); |
288 | led_classdev_unregister(&led->cdev); |
289 | return ret; |
290 | } |
291 | |
292 | return 0; |
293 | } |
294 | |
295 | static int __devexit max8997_led_remove(struct platform_device *pdev) |
296 | { |
297 | struct max8997_led *led = platform_get_drvdata(pdev); |
298 | |
299 | device_remove_file(led->cdev.dev, &dev_attr_mode); |
300 | led_classdev_unregister(&led->cdev); |
301 | |
302 | return 0; |
303 | } |
304 | |
305 | static struct platform_driver max8997_led_driver = { |
306 | .driver = { |
307 | .name = "max8997-led", |
308 | .owner = THIS_MODULE, |
309 | }, |
310 | .probe = max8997_led_probe, |
311 | .remove = __devexit_p(max8997_led_remove), |
312 | }; |
313 | |
314 | module_platform_driver(max8997_led_driver); |
315 | |
316 | MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); |
317 | MODULE_DESCRIPTION("MAX8997 LED driver"); |
318 | MODULE_LICENSE("GPL"); |
319 | MODULE_ALIAS("platform:max8997-led"); |
320 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9