• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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