]>
Commit | Line | Data |
---|---|---|
bb6febdc MG |
1 | /* |
2 | * Copyright 2013 Maximilian Güntner <maximilian.guentner@gmail.com> | |
3 | * | |
4 | * This file is subject to the terms and conditions of version 2 of | |
5 | * the GNU General Public License. See the file COPYING in the main | |
6 | * directory of this archive for more details. | |
7 | * | |
8 | * Based on leds-pca963x.c driver by | |
9 | * Peter Meerwald <p.meerwald@bct-electronic.com> | |
10 | * | |
11 | * Driver for the NXP PCA9685 12-Bit PWM LED driver chip. | |
12 | * | |
13 | */ | |
14 | ||
15 | #include <linux/ctype.h> | |
16 | #include <linux/delay.h> | |
17 | #include <linux/err.h> | |
18 | #include <linux/i2c.h> | |
19 | #include <linux/leds.h> | |
20 | #include <linux/module.h> | |
21 | #include <linux/slab.h> | |
22 | #include <linux/string.h> | |
23 | #include <linux/workqueue.h> | |
24 | ||
25 | #include <linux/platform_data/leds-pca9685.h> | |
26 | ||
27 | /* Register Addresses */ | |
28 | #define PCA9685_MODE1 0x00 | |
29 | #define PCA9685_MODE2 0x01 | |
30 | #define PCA9685_LED0_ON_L 0x06 | |
31 | #define PCA9685_ALL_LED_ON_L 0xFA | |
32 | ||
33 | /* MODE1 Register */ | |
34 | #define PCA9685_ALLCALL 0x00 | |
35 | #define PCA9685_SLEEP 0x04 | |
36 | #define PCA9685_AI 0x05 | |
37 | ||
38 | /* MODE2 Register */ | |
39 | #define PCA9685_INVRT 0x04 | |
40 | #define PCA9685_OUTDRV 0x02 | |
41 | ||
42 | static const struct i2c_device_id pca9685_id[] = { | |
43 | { "pca9685", 0 }, | |
44 | { } | |
45 | }; | |
46 | MODULE_DEVICE_TABLE(i2c, pca9685_id); | |
47 | ||
48 | struct pca9685_led { | |
49 | struct i2c_client *client; | |
50 | struct work_struct work; | |
51 | u16 brightness; | |
52 | struct led_classdev led_cdev; | |
53 | int led_num; /* 0-15 */ | |
54 | char name[32]; | |
55 | }; | |
56 | ||
57 | static void pca9685_write_msg(struct i2c_client *client, u8 *buf, u8 len) | |
58 | { | |
59 | struct i2c_msg msg = { | |
60 | .addr = client->addr, | |
61 | .flags = 0x00, | |
62 | .len = len, | |
63 | .buf = buf | |
64 | }; | |
65 | i2c_transfer(client->adapter, &msg, 1); | |
66 | } | |
67 | ||
68 | static void pca9685_all_off(struct i2c_client *client) | |
69 | { | |
70 | u8 i2c_buffer[5] = {PCA9685_ALL_LED_ON_L, 0x00, 0x00, 0x00, 0x10}; | |
71 | pca9685_write_msg(client, i2c_buffer, 5); | |
72 | } | |
73 | ||
74 | static void pca9685_led_work(struct work_struct *work) | |
75 | { | |
76 | struct pca9685_led *pca9685; | |
77 | u8 i2c_buffer[5]; | |
78 | ||
79 | pca9685 = container_of(work, struct pca9685_led, work); | |
80 | i2c_buffer[0] = PCA9685_LED0_ON_L + 4 * pca9685->led_num; | |
81 | /* | |
82 | * 4095 is the maximum brightness, so we set the ON time to 0x1000 | |
83 | * which disables the PWM generator for that LED | |
84 | */ | |
85 | if (pca9685->brightness == 4095) | |
86 | *((__le16 *)(i2c_buffer+1)) = cpu_to_le16(0x1000); | |
87 | else | |
88 | *((__le16 *)(i2c_buffer+1)) = 0x0000; | |
89 | ||
90 | if (pca9685->brightness == 0) | |
91 | *((__le16 *)(i2c_buffer+3)) = cpu_to_le16(0x1000); | |
92 | else if (pca9685->brightness == 4095) | |
93 | *((__le16 *)(i2c_buffer+3)) = 0x0000; | |
94 | else | |
95 | *((__le16 *)(i2c_buffer+3)) = cpu_to_le16(pca9685->brightness); | |
96 | ||
97 | pca9685_write_msg(pca9685->client, i2c_buffer, 5); | |
98 | } | |
99 | ||
100 | static void pca9685_led_set(struct led_classdev *led_cdev, | |
101 | enum led_brightness value) | |
102 | { | |
103 | struct pca9685_led *pca9685; | |
104 | pca9685 = container_of(led_cdev, struct pca9685_led, led_cdev); | |
105 | pca9685->brightness = value; | |
106 | ||
107 | schedule_work(&pca9685->work); | |
108 | } | |
109 | ||
110 | static int pca9685_probe(struct i2c_client *client, | |
111 | const struct i2c_device_id *id) | |
112 | { | |
113 | struct pca9685_led *pca9685; | |
114 | struct pca9685_platform_data *pdata; | |
115 | int err; | |
116 | u8 i; | |
117 | ||
118 | pdata = dev_get_platdata(&client->dev); | |
119 | if (pdata) { | |
120 | if (pdata->leds.num_leds < 1 || pdata->leds.num_leds > 15) { | |
121 | dev_err(&client->dev, "board info must claim 1-16 LEDs"); | |
122 | return -EINVAL; | |
123 | } | |
124 | } | |
125 | ||
126 | pca9685 = devm_kzalloc(&client->dev, 16 * sizeof(*pca9685), GFP_KERNEL); | |
127 | if (!pca9685) | |
128 | return -ENOMEM; | |
129 | ||
130 | i2c_set_clientdata(client, pca9685); | |
131 | pca9685_all_off(client); | |
132 | ||
133 | for (i = 0; i < 16; i++) { | |
134 | pca9685[i].client = client; | |
135 | pca9685[i].led_num = i; | |
136 | pca9685[i].name[0] = '\0'; | |
137 | if (pdata && i < pdata->leds.num_leds) { | |
138 | if (pdata->leds.leds[i].name) | |
139 | strncpy(pca9685[i].name, | |
140 | pdata->leds.leds[i].name, | |
141 | sizeof(pca9685[i].name)-1); | |
142 | if (pdata->leds.leds[i].default_trigger) | |
143 | pca9685[i].led_cdev.default_trigger = | |
144 | pdata->leds.leds[i].default_trigger; | |
145 | } | |
146 | if (strlen(pca9685[i].name) == 0) { | |
147 | /* | |
148 | * Write adapter and address to the name as well. | |
149 | * Otherwise multiple chips attached to one host would | |
150 | * not work. | |
151 | */ | |
152 | snprintf(pca9685[i].name, sizeof(pca9685[i].name), | |
153 | "pca9685:%d:x%.2x:%d", | |
154 | client->adapter->nr, client->addr, i); | |
155 | } | |
156 | pca9685[i].led_cdev.name = pca9685[i].name; | |
157 | pca9685[i].led_cdev.max_brightness = 0xfff; | |
158 | pca9685[i].led_cdev.brightness_set = pca9685_led_set; | |
159 | ||
160 | INIT_WORK(&pca9685[i].work, pca9685_led_work); | |
161 | err = led_classdev_register(&client->dev, &pca9685[i].led_cdev); | |
162 | if (err < 0) | |
163 | goto exit; | |
164 | } | |
165 | ||
166 | if (pdata) | |
167 | i2c_smbus_write_byte_data(client, PCA9685_MODE2, | |
168 | pdata->outdrv << PCA9685_OUTDRV | | |
169 | pdata->inverted << PCA9685_INVRT); | |
170 | else | |
171 | i2c_smbus_write_byte_data(client, PCA9685_MODE2, | |
172 | PCA9685_TOTEM_POLE << PCA9685_OUTDRV); | |
173 | /* Enable Auto-Increment, enable oscillator, ALLCALL/SUBADDR disabled */ | |
174 | i2c_smbus_write_byte_data(client, PCA9685_MODE1, BIT(PCA9685_AI)); | |
175 | ||
176 | return 0; | |
177 | ||
178 | exit: | |
179 | while (i--) { | |
180 | led_classdev_unregister(&pca9685[i].led_cdev); | |
181 | cancel_work_sync(&pca9685[i].work); | |
182 | } | |
183 | return err; | |
184 | } | |
185 | ||
186 | static int pca9685_remove(struct i2c_client *client) | |
187 | { | |
188 | struct pca9685_led *pca9685 = i2c_get_clientdata(client); | |
189 | u8 i; | |
190 | ||
191 | for (i = 0; i < 16; i++) { | |
192 | led_classdev_unregister(&pca9685[i].led_cdev); | |
193 | cancel_work_sync(&pca9685[i].work); | |
194 | } | |
195 | pca9685_all_off(client); | |
196 | return 0; | |
197 | } | |
198 | ||
199 | static struct i2c_driver pca9685_driver = { | |
200 | .driver = { | |
201 | .name = "leds-pca9685", | |
202 | .owner = THIS_MODULE, | |
203 | }, | |
204 | .probe = pca9685_probe, | |
205 | .remove = pca9685_remove, | |
206 | .id_table = pca9685_id, | |
207 | }; | |
208 | ||
209 | module_i2c_driver(pca9685_driver); | |
210 | ||
211 | MODULE_AUTHOR("Maximilian Güntner <maximilian.guentner@gmail.com>"); | |
212 | MODULE_DESCRIPTION("PCA9685 LED Driver"); | |
213 | MODULE_LICENSE("GPL v2"); |