leds-lp55xx: use lp55xx_set_brightness()
[deliverable/linux.git] / drivers / leds / leds-lp55xx-common.c
CommitLineData
c93d08fa
MWK
1/*
2 * LP5521/LP5523/LP55231 Common Driver
3 *
4 * Copyright 2012 Texas Instruments
5 *
6 * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
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 * Derived from leds-lp5521.c, leds-lp5523.c
13 */
14
a85908dd 15#include <linux/delay.h>
c93d08fa
MWK
16#include <linux/i2c.h>
17#include <linux/leds.h>
18#include <linux/module.h>
19#include <linux/platform_data/leds-lp55xx.h>
20
21#include "leds-lp55xx-common.h"
22
a6e4679a
MWK
23static struct lp55xx_led *cdev_to_lp55xx_led(struct led_classdev *cdev)
24{
25 return container_of(cdev, struct lp55xx_led, cdev);
26}
27
48068d5d
MWK
28static void lp55xx_reset_device(struct lp55xx_chip *chip)
29{
30 struct lp55xx_device_config *cfg = chip->cfg;
31 u8 addr = cfg->reset.addr;
32 u8 val = cfg->reset.val;
33
34 /* no error checking here because no ACK from the device after reset */
35 lp55xx_write(chip, addr, val);
36}
37
e3a700d8
MWK
38static int lp55xx_detect_device(struct lp55xx_chip *chip)
39{
40 struct lp55xx_device_config *cfg = chip->cfg;
41 u8 addr = cfg->enable.addr;
42 u8 val = cfg->enable.val;
43 int ret;
44
45 ret = lp55xx_write(chip, addr, val);
46 if (ret)
47 return ret;
48
49 usleep_range(1000, 2000);
50
51 ret = lp55xx_read(chip, addr, &val);
52 if (ret)
53 return ret;
54
55 if (val != cfg->enable.val)
56 return -ENODEV;
57
58 return 0;
59}
60
ffbdccdb
MWK
61static int lp55xx_post_init_device(struct lp55xx_chip *chip)
62{
63 struct lp55xx_device_config *cfg = chip->cfg;
64
65 if (!cfg->post_init_device)
66 return 0;
67
68 return cfg->post_init_device(chip);
69}
70
0e202346
MWK
71static struct attribute *lp55xx_led_attributes[] = {
72 NULL,
73};
74
75static struct attribute_group lp55xx_led_attr_group = {
76 .attrs = lp55xx_led_attributes
77};
78
79static void lp55xx_set_brightness(struct led_classdev *cdev,
80 enum led_brightness brightness)
81{
a6e4679a
MWK
82 struct lp55xx_led *led = cdev_to_lp55xx_led(cdev);
83
84 led->brightness = (u8)brightness;
85 schedule_work(&led->brightness_work);
0e202346
MWK
86}
87
9e9b3db1
MWK
88static int lp55xx_init_led(struct lp55xx_led *led,
89 struct lp55xx_chip *chip, int chan)
90{
0e202346
MWK
91 struct lp55xx_platform_data *pdata = chip->pdata;
92 struct lp55xx_device_config *cfg = chip->cfg;
93 struct device *dev = &chip->cl->dev;
94 char name[32];
95 int ret;
96 int max_channel = cfg->max_channel;
97
98 if (chan >= max_channel) {
99 dev_err(dev, "invalid channel: %d / %d\n", chan, max_channel);
100 return -EINVAL;
101 }
102
103 if (pdata->led_config[chan].led_current == 0)
104 return 0;
105
106 led->led_current = pdata->led_config[chan].led_current;
107 led->max_current = pdata->led_config[chan].max_current;
108 led->chan_nr = pdata->led_config[chan].chan_nr;
109
110 if (led->chan_nr >= max_channel) {
111 dev_err(dev, "Use channel numbers between 0 and %d\n",
112 max_channel - 1);
113 return -EINVAL;
114 }
115
116 led->cdev.brightness_set = lp55xx_set_brightness;
117
118 if (pdata->led_config[chan].name) {
119 led->cdev.name = pdata->led_config[chan].name;
120 } else {
121 snprintf(name, sizeof(name), "%s:channel%d",
122 pdata->label ? : chip->cl->name, chan);
123 led->cdev.name = name;
124 }
125
126 /*
127 * register led class device for each channel and
128 * add device attributes
129 */
130
131 ret = led_classdev_register(dev, &led->cdev);
132 if (ret) {
133 dev_err(dev, "led register err: %d\n", ret);
134 return ret;
135 }
136
137 ret = sysfs_create_group(&led->cdev.dev->kobj, &lp55xx_led_attr_group);
138 if (ret) {
139 dev_err(dev, "led sysfs err: %d\n", ret);
140 led_classdev_unregister(&led->cdev);
141 return ret;
142 }
143
9e9b3db1
MWK
144 return 0;
145}
146
c93d08fa
MWK
147int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val)
148{
149 return i2c_smbus_write_byte_data(chip->cl, reg, val);
150}
151EXPORT_SYMBOL_GPL(lp55xx_write);
152
153int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val)
154{
155 s32 ret;
156
157 ret = i2c_smbus_read_byte_data(chip->cl, reg);
158 if (ret < 0)
159 return ret;
160
161 *val = ret;
162 return 0;
163}
164EXPORT_SYMBOL_GPL(lp55xx_read);
165
166int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val)
167{
168 int ret;
169 u8 tmp;
170
171 ret = lp55xx_read(chip, reg, &tmp);
172 if (ret)
173 return ret;
174
175 tmp &= ~mask;
176 tmp |= val & mask;
177
178 return lp55xx_write(chip, reg, tmp);
179}
180EXPORT_SYMBOL_GPL(lp55xx_update_bits);
181
a85908dd
MWK
182int lp55xx_init_device(struct lp55xx_chip *chip)
183{
184 struct lp55xx_platform_data *pdata;
48068d5d 185 struct lp55xx_device_config *cfg;
a85908dd
MWK
186 struct device *dev = &chip->cl->dev;
187 int ret = 0;
188
189 WARN_ON(!chip);
190
191 pdata = chip->pdata;
48068d5d 192 cfg = chip->cfg;
a85908dd 193
48068d5d 194 if (!pdata || !cfg)
a85908dd
MWK
195 return -EINVAL;
196
197 if (pdata->setup_resources) {
198 ret = pdata->setup_resources();
199 if (ret < 0) {
200 dev_err(dev, "setup resoure err: %d\n", ret);
201 goto err;
202 }
203 }
204
205 if (pdata->enable) {
206 pdata->enable(0);
207 usleep_range(1000, 2000); /* Keep enable down at least 1ms */
208 pdata->enable(1);
209 usleep_range(1000, 2000); /* 500us abs min. */
210 }
211
48068d5d
MWK
212 lp55xx_reset_device(chip);
213
214 /*
215 * Exact value is not available. 10 - 20ms
216 * appears to be enough for reset.
217 */
218 usleep_range(10000, 20000);
219
e3a700d8
MWK
220 ret = lp55xx_detect_device(chip);
221 if (ret) {
222 dev_err(dev, "device detection err: %d\n", ret);
223 goto err;
224 }
225
ffbdccdb
MWK
226 /* chip specific initialization */
227 ret = lp55xx_post_init_device(chip);
22ebeb48
MWK
228 if (ret) {
229 dev_err(dev, "post init device err: %d\n", ret);
230 goto err_post_init;
231 }
ffbdccdb
MWK
232
233 return 0;
234
22ebeb48 235err_post_init:
6ce61762
MWK
236 lp55xx_deinit_device(chip);
237err:
238 return ret;
239}
240EXPORT_SYMBOL_GPL(lp55xx_init_device);
241
242void lp55xx_deinit_device(struct lp55xx_chip *chip)
243{
244 struct lp55xx_platform_data *pdata = chip->pdata;
245
22ebeb48
MWK
246 if (pdata->enable)
247 pdata->enable(0);
6ce61762 248
22ebeb48
MWK
249 if (pdata->release_resources)
250 pdata->release_resources();
a85908dd 251}
6ce61762 252EXPORT_SYMBOL_GPL(lp55xx_deinit_device);
a85908dd 253
9e9b3db1
MWK
254int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip)
255{
256 struct lp55xx_platform_data *pdata = chip->pdata;
257 struct lp55xx_device_config *cfg = chip->cfg;
258 int num_channels = pdata->num_channels;
259 struct lp55xx_led *each;
260 u8 led_current;
261 int ret;
262 int i;
263
264 if (!cfg->brightness_work_fn) {
265 dev_err(&chip->cl->dev, "empty brightness configuration\n");
266 return -EINVAL;
267 }
268
269 for (i = 0; i < num_channels; i++) {
270
271 /* do not initialize channels that are not connected */
272 if (pdata->led_config[i].led_current == 0)
273 continue;
274
275 led_current = pdata->led_config[i].led_current;
276 each = led + i;
277 ret = lp55xx_init_led(each, chip, i);
278 if (ret)
279 goto err_init_led;
280
281 INIT_WORK(&each->brightness_work, cfg->brightness_work_fn);
282
283 chip->num_leds++;
284 each->chip = chip;
285
286 /* setting led current at each channel */
287 if (cfg->set_led_current)
288 cfg->set_led_current(each, led_current);
289 }
290
291 return 0;
292
293err_init_led:
294 for (i = 0; i < chip->num_leds; i++) {
295 each = led + i;
296 led_classdev_unregister(&each->cdev);
297 flush_work(&each->brightness_work);
298 }
299 return ret;
300}
301EXPORT_SYMBOL_GPL(lp55xx_register_leds);
302
c93d08fa
MWK
303MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");
304MODULE_DESCRIPTION("LP55xx Common Driver");
305MODULE_LICENSE("GPL");
This page took 0.040395 seconds and 5 git commands to generate.