1 /* pca963x.c - 4-bit I2C-bus LED driver
2 *
3 * Copyright (C) 2008 HTC Corporation.
4 * Author: Shan-Fu Chiou <sfchiou@gmail.com>
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
17 #include <linux/module.h>
18 #include <linux/init.h>
19 #include <linux/slab.h>
20 #include <linux/i2c.h>
21 #include <linux/delay.h>
22 #include <linux/device.h>
23 #include <linux/leds.h>
24 #include <linux/spinlock.h>
25 #include <linux/workqueue.h>
26
27 static uint8_t address[] = { 0x02, 0x03, 0x04 };
28 static DEFINE_SPINLOCK(pca963x_lock);
29
30 enum op_t {
31 OP_SET_BLINK,
32 OP_SET_GRPPWM,
33 OP_SET_GRPFREQ,
34 OP_SET_BLUE_BRIGHTNESS,
35 OP_SET_GREEN_BRIGHTNESS,
36 OP_SET_RED_BRIGHTNESS,
37 };
38
39 enum power_mode {
40 MODE_SLEEP,
41 MODE_NORMAL,
42 };
43
44 struct pca963x_t {
45 uint8_t colors[3];
46 uint8_t blink;
47 uint8_t grppwm;
48 uint8_t grpfreq;
49 };
50
51 struct pca963x_data {
52 struct pca963x_t data;
53 uint8_t dirty;
54 uint8_t status;
55 enum power_mode mode;
56 struct work_struct work;
57 struct i2c_client *client;
58 struct led_classdev leds[3]; /* blue, green, red */
59 };
60
pca963x_blink_show(struct device * dev,struct device_attribute * attr,char * buf)61 static ssize_t pca963x_blink_show(struct device *dev,
62 struct device_attribute *attr, char *buf)
63 {
64 struct i2c_client *client = to_i2c_client(dev);
65 struct pca963x_data *pca963x = i2c_get_clientdata(client);
66
67 if (((pca963x->dirty >> OP_SET_BLINK) & 0x01))
68 flush_scheduled_work();
69 return sprintf(buf, "%u\n", pca963x->data.blink);
70 }
71
pca963x_blink_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)72 static ssize_t pca963x_blink_store(struct device *dev,
73 struct device_attribute *attr,
74 const char *buf, size_t count)
75 {
76 struct i2c_client *client = to_i2c_client(dev);
77 struct pca963x_data *pca963x = i2c_get_clientdata(client);
78
79 int val = -1;
80
81 sscanf(buf, "%u", &val);
82 if (val < 0 || val > 1)
83 return -EINVAL;
84
85 spin_lock(&pca963x_lock);
86 pca963x->dirty |= 1 << OP_SET_BLINK;
87 pca963x->data.blink = val;
88 spin_unlock(&pca963x_lock);
89 schedule_work(&pca963x->work);
90
91 return count;
92 }
93
94 static DEVICE_ATTR(blink, 0644, pca963x_blink_show, pca963x_blink_store);
95
pca963x_grpfreq_show(struct device * dev,struct device_attribute * attr,char * buf)96 static ssize_t pca963x_grpfreq_show(struct device *dev,
97 struct device_attribute *attr, char *buf)
98 {
99 struct i2c_client *client = to_i2c_client(dev);
100 struct pca963x_data *pca963x = i2c_get_clientdata(client);
101
102 if (((pca963x->dirty >> OP_SET_GRPFREQ) & 0x01))
103 flush_scheduled_work();
104 return sprintf(buf, "%u\n", pca963x->data.grpfreq);
105 }
106
pca963x_grpfreq_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)107 static ssize_t pca963x_grpfreq_store(struct device *dev,
108 struct device_attribute *attr,
109 const char *buf, size_t count)
110 {
111 struct i2c_client *client = to_i2c_client(dev);
112 struct pca963x_data *pca963x = i2c_get_clientdata(client);
113
114 unsigned long val = simple_strtoul(buf, NULL, 10);
115
116 if (val > 0xff)
117 return -EINVAL;
118
119 spin_lock(&pca963x_lock);
120 pca963x->dirty |= 1 << OP_SET_GRPFREQ;
121 pca963x->data.grpfreq = val;
122 spin_unlock(&pca963x_lock);
123 schedule_work(&pca963x->work);
124
125 return count;
126 }
127
128 static DEVICE_ATTR(grpfreq, 0644, pca963x_grpfreq_show, pca963x_grpfreq_store);
129
pca963x_grppwm_show(struct device * dev,struct device_attribute * attr,char * buf)130 static ssize_t pca963x_grppwm_show(struct device *dev,
131 struct device_attribute *attr, char *buf)
132 {
133 struct i2c_client *client = to_i2c_client(dev);
134 struct pca963x_data *pca963x = i2c_get_clientdata(client);
135
136 if (((pca963x->dirty >> OP_SET_GRPPWM) & 0x01))
137 flush_scheduled_work();
138 return sprintf(buf, "%u\n", pca963x->data.grppwm);
139 }
140
pca963x_grppwm_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)141 static ssize_t pca963x_grppwm_store(struct device *dev,
142 struct device_attribute *attr,
143 const char *buf, size_t count)
144 {
145 struct i2c_client *client = to_i2c_client(dev);
146 struct pca963x_data *pca963x = i2c_get_clientdata(client);
147
148 unsigned long val = simple_strtoul(buf, NULL, 10);
149
150 if (val > 0xff)
151 return -EINVAL;
152
153 spin_lock(&pca963x_lock);
154 pca963x->dirty |= 1 << OP_SET_GRPPWM;
155 pca963x->data.grppwm = val;
156 spin_unlock(&pca963x_lock);
157 schedule_work(&pca963x->work);
158
159 return count;
160 }
161
162 static DEVICE_ATTR(grppwm, 0644, pca963x_grppwm_show, pca963x_grppwm_store);
163
led_brightness_set(struct led_classdev * led_cdev,enum led_brightness brightness)164 static void led_brightness_set(struct led_classdev *led_cdev,
165 enum led_brightness brightness)
166 {
167 struct pca963x_data *pca963x;
168 int idx = 2;
169
170 spin_lock(&pca963x_lock);
171 if (!strcmp(led_cdev->name, "blue")) {
172 idx = 0;
173 } else if (!strcmp(led_cdev->name, "green")) {
174 idx = 1;
175 } else {
176 idx = 2;
177 }
178 pca963x = container_of(led_cdev, struct pca963x_data, leds[idx]);
179 pca963x->data.colors[idx] = brightness;
180 pca963x->dirty |= (1 << (OP_SET_BLUE_BRIGHTNESS + idx));
181 spin_unlock(&pca963x_lock);
182
183 schedule_work(&pca963x->work);
184
185 }
186
pca963x_update_brightness(struct pca963x_data * pca963x,int idx,int brightness)187 static void pca963x_update_brightness(struct pca963x_data *pca963x, int idx,
188 int brightness)
189 {
190 if (brightness > LED_OFF) {
191 if (brightness == LED_FULL) {
192 pca963x->status &= ~(1 << idx);
193 pca963x->status |= (1 << (idx + 4));
194 } else {
195 pca963x->status |= (1 << idx);
196 pca963x->status &= ~(1 << (idx + 4));
197 }
198 } else {
199 pca963x->status &= ~(1 << idx);
200 pca963x->status &= ~(1 << (idx + 4));
201 }
202 i2c_smbus_write_byte_data(pca963x->client, address[idx], brightness);
203 }
204
pca963x_work_func(struct work_struct * work)205 static void pca963x_work_func(struct work_struct *work)
206 {
207 int ret;
208 uint8_t dirty = 0;
209 struct pca963x_t work_data;
210 struct pca963x_data *pca963x =
211 container_of(work, struct pca963x_data, work);
212
213 spin_lock(&pca963x_lock);
214 work_data = pca963x->data;
215 dirty = pca963x->dirty;
216 pca963x->dirty = 0;
217 spin_unlock(&pca963x_lock);
218
219 ret = i2c_smbus_read_byte_data(pca963x->client, 0x00);
220 /* check if should switch to normal mode */
221 if (!pca963x->mode) {
222 i2c_smbus_write_byte_data(pca963x->client, 0x00, 0x01);
223 pca963x->mode = MODE_NORMAL;
224 i2c_smbus_write_byte_data(pca963x->client, 0x08, 0xFF);
225 }
226
227 if ((dirty >> OP_SET_BLINK) & 0x01) {
228 ret = i2c_smbus_read_byte_data(pca963x->client, 0x01);
229 if (work_data.blink) /* enable blinking */
230 i2c_smbus_write_byte_data(pca963x->client, 0x01,
231 ret | 0x20);
232 else {
233 /* set group duty cycle control to default */
234 i2c_smbus_write_byte_data(pca963x->client, 0x06, 0xFF);
235 /* set group frequency to default */
236 i2c_smbus_write_byte_data(pca963x->client, 0x07, 0x00);
237 /* enable dimming */
238 i2c_smbus_write_byte_data(pca963x->client, 0x01,
239 ret & 0xDF);
240 }
241 }
242
243 if ((dirty >> OP_SET_GRPPWM) & 0x01) {
244 i2c_smbus_write_byte_data(pca963x->client, 0x06,
245 work_data.grppwm);
246 }
247
248 if ((dirty >> OP_SET_GRPFREQ) & 0x01) {
249 i2c_smbus_write_byte_data(pca963x->client, 0x07,
250 work_data.grpfreq);
251 }
252
253 if ((dirty >> OP_SET_BLUE_BRIGHTNESS) & 0x01)
254 pca963x_update_brightness(pca963x, 0, work_data.colors[0]);
255
256 if ((dirty >> OP_SET_GREEN_BRIGHTNESS) & 0x01)
257 pca963x_update_brightness(pca963x, 1, work_data.colors[1]);
258
259 if ((dirty >> OP_SET_RED_BRIGHTNESS) & 0x01)
260 pca963x_update_brightness(pca963x, 2, work_data.colors[2]);
261
262 /* check if could go to low power mode */
263 if (((pca963x->status & 0x0F) == 0) && (!work_data.blink)) {
264 i2c_smbus_write_byte_data(pca963x->client, 0x08, 0xAA);
265 i2c_smbus_write_byte_data(pca963x->client, 0x00, 0x11);
266 pca963x->mode = MODE_SLEEP;
267 }
268 }
269
set_pca963x_default(struct i2c_client * client)270 static void set_pca963x_default(struct i2c_client *client)
271 {
272 i2c_smbus_write_byte_data(client, 0x00, 0x01);
273 i2c_smbus_write_byte_data(client, 0x01, 0x00);
274 /* set all LEDx brightness off */
275 i2c_smbus_write_byte_data(client, address[0], LED_OFF);
276 i2c_smbus_write_byte_data(client, address[1], LED_OFF);
277 i2c_smbus_write_byte_data(client, address[2], LED_OFF);
278 /* set group duty cycle control to default */
279 i2c_smbus_write_byte_data(client, 0x06, 0xFF);
280 /* set group frequency to default */
281 i2c_smbus_write_byte_data(client, 0x07, 0x00);
282 /*
283 * set LEDx individual brightness and group dimming/blinking
284 * can be controlled by * its PWMx register and GRPPWM registers.
285 */
286 i2c_smbus_write_byte_data(client, 0x08, 0xFF);
287 /* low power mode. oscillator off */
288 i2c_smbus_write_byte_data(client, 0x00, 0x11);
289 }
290
pca963x_probe(struct i2c_client * client,const struct i2c_device_id * id)291 static int pca963x_probe(struct i2c_client *client,
292 const struct i2c_device_id *id)
293 {
294 int ret = 0;
295
296 struct pca963x_data *pca963x;
297
298 if (!i2c_check_functionality(client->adapter,
299 I2C_FUNC_SMBUS_BYTE_DATA)) {
300 ret = -ENODEV;
301 goto exit;
302 }
303
304 pca963x = kzalloc(sizeof(struct pca963x_data), GFP_KERNEL);
305 if (pca963x == NULL) {
306 ret = -ENOMEM;
307 goto err_alloc_failed;
308 }
309
310 INIT_WORK(&pca963x->work, pca963x_work_func);
311
312 pca963x->client = client;
313
314 pca963x->leds[0].name = "blue";
315 pca963x->leds[0].brightness = LED_OFF;
316 pca963x->leds[0].brightness_set = led_brightness_set;
317
318 pca963x->leds[1].name = "green";
319 pca963x->leds[1].brightness = LED_OFF;
320 pca963x->leds[1].brightness_set = led_brightness_set;
321
322 pca963x->leds[2].name = "red";
323 pca963x->leds[2].brightness = LED_OFF;
324 pca963x->leds[2].brightness_set = led_brightness_set;
325
326 pca963x->dirty = 0;
327 pca963x->status = 0;
328
329 pca963x->data.colors[0] = LED_OFF;
330 pca963x->data.colors[1] = LED_OFF;
331 pca963x->data.colors[2] = LED_OFF;
332 pca963x->data.blink = 0;
333 pca963x->data.grppwm = 0;
334 pca963x->data.grpfreq = 0;
335 i2c_set_clientdata(client, pca963x);
336
337 set_pca963x_default(client);
338 pca963x->mode = MODE_SLEEP;
339
340 /* blue */
341 ret = led_classdev_register(&client->dev, &pca963x->leds[0]);
342 if (ret < 0) {
343 printk(KERN_ERR "pca963x: led_classdev_register failed\n");
344 goto err_led0_classdev_register_failed;
345 }
346 /* green */
347 ret = led_classdev_register(&client->dev, &pca963x->leds[1]);
348 if (ret < 0) {
349 printk(KERN_ERR "pca963x: led_classdev_register failed\n");
350 goto err_led1_classdev_register_failed;
351 }
352 /* red */
353 ret = led_classdev_register(&client->dev, &pca963x->leds[2]);
354 if (ret < 0) {
355 printk(KERN_ERR "pca963x: led_classdev_register failed\n");
356 goto err_led2_classdev_register_failed;
357 }
358
359 ret = device_create_file(&client->dev, &dev_attr_blink);
360 ret = device_create_file(&client->dev, &dev_attr_grppwm);
361 ret = device_create_file(&client->dev, &dev_attr_grpfreq);
362
363 return 0;
364
365 err_led2_classdev_register_failed:
366 led_classdev_unregister(&pca963x->leds[2]);
367 err_led1_classdev_register_failed:
368 led_classdev_unregister(&pca963x->leds[1]);
369 err_led0_classdev_register_failed:
370 led_classdev_unregister(&pca963x->leds[0]);
371 err_alloc_failed:
372 kfree(pca963x);
373 exit:
374 return ret;
375 }
376
pca963x_suspend(struct i2c_client * client,pm_message_t mesg)377 static int pca963x_suspend(struct i2c_client *client, pm_message_t mesg)
378 {
379 flush_scheduled_work();
380 return 0;
381 }
382
pca963x_remove(struct i2c_client * client)383 static int pca963x_remove(struct i2c_client *client)
384 {
385 struct pca963x_data *pca963x = i2c_get_clientdata(client);
386
387 cancel_work_sync(&pca963x->work);
388 device_remove_file(&client->dev, &dev_attr_blink);
389 device_remove_file(&client->dev, &dev_attr_grppwm);
390 device_remove_file(&client->dev, &dev_attr_grpfreq);
391 set_pca963x_default(client);
392 led_classdev_unregister(&pca963x->leds[0]);
393 led_classdev_unregister(&pca963x->leds[1]);
394 led_classdev_unregister(&pca963x->leds[2]);
395 i2c_detach_client(client);
396
397 kfree(pca963x);
398 return 0;
399 }
400
401 static const struct i2c_device_id pca963x_id[] = {
402 { "pca963x", 0 },
403 { }
404 };
405
406 static struct i2c_driver pca963x_driver = {
407 .driver = {
408 .name = "pca963x",
409 },
410 .probe = pca963x_probe,
411 .suspend = pca963x_suspend,
412 .remove = pca963x_remove,
413 .id_table = pca963x_id,
414 };
415
pca963x_init(void)416 static int __init pca963x_init(void)
417 {
418 return i2c_add_driver(&pca963x_driver);
419 }
420
pca963x_exit(void)421 static void __exit pca963x_exit(void)
422 {
423 i2c_del_driver(&pca963x_driver);
424 }
425
426 MODULE_AUTHOR("Shan-Fu Chiou <sfchiou@gmail.com>");
427 MODULE_DESCRIPTION("pca963x driver");
428 MODULE_LICENSE("GPL");
429
430 module_init(pca963x_init);
431 module_exit(pca963x_exit);
432