blob: 6c888e39e4cd9ceb36cdf7b9f73566521a17eaf1 [file] [log] [blame]
G.Shark Jeong0f598582012-10-04 17:12:55 -07001/*
2* Simple driver for Texas Instruments LM3639 Backlight + Flash LED driver chip
3* Copyright (C) 2012 Texas Instruments
4*
5* This program is free software; you can redistribute it and/or modify
6* it under the terms of the GNU General Public License version 2 as
7* published by the Free Software Foundation.
8*
9*/
10#include <linux/module.h>
11#include <linux/slab.h>
12#include <linux/i2c.h>
13#include <linux/leds.h>
14#include <linux/backlight.h>
15#include <linux/err.h>
16#include <linux/delay.h>
17#include <linux/uaccess.h>
18#include <linux/interrupt.h>
19#include <linux/regmap.h>
20#include <linux/platform_data/lm3639_bl.h>
21
22#define REG_DEV_ID 0x00
23#define REG_CHECKSUM 0x01
24#define REG_BL_CONF_1 0x02
25#define REG_BL_CONF_2 0x03
26#define REG_BL_CONF_3 0x04
27#define REG_BL_CONF_4 0x05
28#define REG_FL_CONF_1 0x06
29#define REG_FL_CONF_2 0x07
30#define REG_FL_CONF_3 0x08
31#define REG_IO_CTRL 0x09
32#define REG_ENABLE 0x0A
33#define REG_FLAG 0x0B
34#define REG_MAX REG_FLAG
35
36struct lm3639_chip_data {
37 struct device *dev;
38 struct lm3639_platform_data *pdata;
39
40 struct backlight_device *bled;
41 struct led_classdev cdev_flash;
42 struct led_classdev cdev_torch;
43 struct regmap *regmap;
44
45 unsigned int bled_mode;
46 unsigned int bled_map;
47 unsigned int last_flag;
48};
49
50/* initialize chip */
Bill Pemberton1b9e4502012-11-19 13:21:46 -050051static int lm3639_chip_init(struct lm3639_chip_data *pchip)
G.Shark Jeong0f598582012-10-04 17:12:55 -070052{
53 int ret;
54 unsigned int reg_val;
55 struct lm3639_platform_data *pdata = pchip->pdata;
56
57 /* input pins config. */
58 ret =
59 regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x08,
60 pdata->pin_pwm);
61 if (ret < 0)
62 goto out;
63
64 reg_val = (pdata->pin_pwm & 0x40) | pdata->pin_strobe | pdata->pin_tx;
65 ret = regmap_update_bits(pchip->regmap, REG_IO_CTRL, 0x7C, reg_val);
66 if (ret < 0)
67 goto out;
68
69 /* init brightness */
70 ret = regmap_write(pchip->regmap, REG_BL_CONF_4, pdata->init_brt_led);
71 if (ret < 0)
72 goto out;
73
74 ret = regmap_write(pchip->regmap, REG_BL_CONF_3, pdata->init_brt_led);
75 if (ret < 0)
76 goto out;
77
78 /* output pins config. */
Jingoo Han0ab7b202013-11-12 15:09:02 -080079 if (!pdata->init_brt_led) {
80 reg_val = pdata->fled_pins;
81 reg_val |= pdata->bled_pins;
82 } else {
83 reg_val = pdata->fled_pins;
84 reg_val |= pdata->bled_pins | 0x01;
85 }
G.Shark Jeong0f598582012-10-04 17:12:55 -070086
87 ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x79, reg_val);
88 if (ret < 0)
89 goto out;
90
91 return ret;
92out:
93 dev_err(pchip->dev, "i2c failed to access register\n");
94 return ret;
95}
96
97/* update and get brightness */
98static int lm3639_bled_update_status(struct backlight_device *bl)
99{
100 int ret;
101 unsigned int reg_val;
102 struct lm3639_chip_data *pchip = bl_get_data(bl);
103 struct lm3639_platform_data *pdata = pchip->pdata;
104
105 ret = regmap_read(pchip->regmap, REG_FLAG, &reg_val);
106 if (ret < 0)
107 goto out;
108
109 if (reg_val != 0)
110 dev_info(pchip->dev, "last flag is 0x%x\n", reg_val);
111
112 /* pwm control */
113 if (pdata->pin_pwm) {
114 if (pdata->pwm_set_intensity)
115 pdata->pwm_set_intensity(bl->props.brightness,
116 pdata->max_brt_led);
117 else
118 dev_err(pchip->dev,
119 "No pwm control func. in plat-data\n");
120 return bl->props.brightness;
121 }
122
123 /* i2c control and set brigtness */
124 ret = regmap_write(pchip->regmap, REG_BL_CONF_4, bl->props.brightness);
125 if (ret < 0)
126 goto out;
127 ret = regmap_write(pchip->regmap, REG_BL_CONF_3, bl->props.brightness);
128 if (ret < 0)
129 goto out;
130
131 if (!bl->props.brightness)
132 ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x01, 0x00);
133 else
134 ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x01, 0x01);
135 if (ret < 0)
136 goto out;
137
138 return bl->props.brightness;
139out:
140 dev_err(pchip->dev, "i2c failed to access registers\n");
141 return bl->props.brightness;
142}
143
144static int lm3639_bled_get_brightness(struct backlight_device *bl)
145{
146 int ret;
147 unsigned int reg_val;
148 struct lm3639_chip_data *pchip = bl_get_data(bl);
149 struct lm3639_platform_data *pdata = pchip->pdata;
150
151 if (pdata->pin_pwm) {
152 if (pdata->pwm_get_intensity)
153 bl->props.brightness = pdata->pwm_get_intensity();
154 else
155 dev_err(pchip->dev,
156 "No pwm control func. in plat-data\n");
157 return bl->props.brightness;
158 }
159
160 ret = regmap_read(pchip->regmap, REG_BL_CONF_1, &reg_val);
161 if (ret < 0)
162 goto out;
163 if (reg_val & 0x10)
164 ret = regmap_read(pchip->regmap, REG_BL_CONF_4, &reg_val);
165 else
166 ret = regmap_read(pchip->regmap, REG_BL_CONF_3, &reg_val);
167 if (ret < 0)
168 goto out;
169 bl->props.brightness = reg_val;
170
171 return bl->props.brightness;
172out:
173 dev_err(pchip->dev, "i2c failed to access register\n");
174 return bl->props.brightness;
175}
176
177static const struct backlight_ops lm3639_bled_ops = {
178 .options = BL_CORE_SUSPENDRESUME,
179 .update_status = lm3639_bled_update_status,
180 .get_brightness = lm3639_bled_get_brightness,
181};
182
183/* backlight mapping mode */
184static ssize_t lm3639_bled_mode_store(struct device *dev,
185 struct device_attribute *devAttr,
186 const char *buf, size_t size)
187{
188 ssize_t ret;
189 struct lm3639_chip_data *pchip = dev_get_drvdata(dev);
190 unsigned int state;
191
192 ret = kstrtouint(buf, 10, &state);
193 if (ret)
194 goto out_input;
195
196 if (!state)
197 ret =
198 regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x10,
199 0x00);
200 else
201 ret =
202 regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x10,
203 0x10);
204
205 if (ret < 0)
206 goto out;
207
208 return size;
209
210out:
211 dev_err(pchip->dev, "%s:i2c access fail to register\n", __func__);
Axel Lindc36d7e2012-10-19 13:56:52 -0700212 return ret;
G.Shark Jeong0f598582012-10-04 17:12:55 -0700213
214out_input:
215 dev_err(pchip->dev, "%s:input conversion fail\n", __func__);
Axel Lindc36d7e2012-10-19 13:56:52 -0700216 return ret;
G.Shark Jeong0f598582012-10-04 17:12:55 -0700217
218}
219
Axel Linfb08cd92012-12-17 16:00:59 -0800220static DEVICE_ATTR(bled_mode, S_IWUSR, NULL, lm3639_bled_mode_store);
G.Shark Jeong0f598582012-10-04 17:12:55 -0700221
222/* torch */
223static void lm3639_torch_brightness_set(struct led_classdev *cdev,
224 enum led_brightness brightness)
225{
226 int ret;
227 unsigned int reg_val;
228 struct lm3639_chip_data *pchip;
229
230 pchip = container_of(cdev, struct lm3639_chip_data, cdev_torch);
231
232 ret = regmap_read(pchip->regmap, REG_FLAG, &reg_val);
233 if (ret < 0)
234 goto out;
235 if (reg_val != 0)
236 dev_info(pchip->dev, "last flag is 0x%x\n", reg_val);
237
238 /* brightness 0 means off state */
239 if (!brightness) {
240 ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x00);
241 if (ret < 0)
242 goto out;
243 return;
244 }
245
246 ret = regmap_update_bits(pchip->regmap,
247 REG_FL_CONF_1, 0x70, (brightness - 1) << 4);
248 if (ret < 0)
249 goto out;
250 ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x02);
251 if (ret < 0)
252 goto out;
253
254 return;
255out:
256 dev_err(pchip->dev, "i2c failed to access register\n");
257 return;
258}
259
260/* flash */
261static void lm3639_flash_brightness_set(struct led_classdev *cdev,
262 enum led_brightness brightness)
263{
264 int ret;
265 unsigned int reg_val;
266 struct lm3639_chip_data *pchip;
267
268 pchip = container_of(cdev, struct lm3639_chip_data, cdev_flash);
269
270 ret = regmap_read(pchip->regmap, REG_FLAG, &reg_val);
271 if (ret < 0)
272 goto out;
273 if (reg_val != 0)
274 dev_info(pchip->dev, "last flag is 0x%x\n", reg_val);
275
276 /* torch off before flash control */
277 ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x00);
278 if (ret < 0)
279 goto out;
280
281 /* brightness 0 means off state */
282 if (!brightness)
283 return;
284
285 ret = regmap_update_bits(pchip->regmap,
286 REG_FL_CONF_1, 0x0F, brightness - 1);
287 if (ret < 0)
288 goto out;
289 ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x06);
290 if (ret < 0)
291 goto out;
292
293 return;
294out:
295 dev_err(pchip->dev, "i2c failed to access register\n");
296 return;
297}
298
299static const struct regmap_config lm3639_regmap = {
300 .reg_bits = 8,
301 .val_bits = 8,
302 .max_register = REG_MAX,
303};
304
Bill Pemberton1b9e4502012-11-19 13:21:46 -0500305static int lm3639_probe(struct i2c_client *client,
G.Shark Jeong0f598582012-10-04 17:12:55 -0700306 const struct i2c_device_id *id)
307{
308 int ret;
309 struct lm3639_chip_data *pchip;
310 struct lm3639_platform_data *pdata = client->dev.platform_data;
311 struct backlight_properties props;
312
313 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
314 dev_err(&client->dev, "i2c functionality check fail.\n");
315 return -EOPNOTSUPP;
316 }
317
318 if (pdata == NULL) {
319 dev_err(&client->dev, "Needs Platform Data.\n");
320 return -ENODATA;
321 }
322
323 pchip = devm_kzalloc(&client->dev,
324 sizeof(struct lm3639_chip_data), GFP_KERNEL);
325 if (!pchip)
326 return -ENOMEM;
327
328 pchip->pdata = pdata;
329 pchip->dev = &client->dev;
330
331 pchip->regmap = devm_regmap_init_i2c(client, &lm3639_regmap);
332 if (IS_ERR(pchip->regmap)) {
333 ret = PTR_ERR(pchip->regmap);
334 dev_err(&client->dev, "fail : allocate register map: %d\n",
335 ret);
336 return ret;
337 }
338 i2c_set_clientdata(client, pchip);
339
340 /* chip initialize */
341 ret = lm3639_chip_init(pchip);
342 if (ret < 0) {
343 dev_err(&client->dev, "fail : chip init\n");
344 goto err_out;
345 }
346
347 /* backlight */
348 props.type = BACKLIGHT_RAW;
349 props.brightness = pdata->init_brt_led;
350 props.max_brightness = pdata->max_brt_led;
351 pchip->bled =
352 backlight_device_register("lm3639_bled", pchip->dev, pchip,
353 &lm3639_bled_ops, &props);
354 if (IS_ERR(pchip->bled)) {
355 dev_err(&client->dev, "fail : backlight register\n");
Devendra Nagaaea00a62013-02-21 16:43:52 -0800356 ret = PTR_ERR(pchip->bled);
G.Shark Jeong0f598582012-10-04 17:12:55 -0700357 goto err_out;
358 }
359
360 ret = device_create_file(&(pchip->bled->dev), &dev_attr_bled_mode);
361 if (ret < 0) {
362 dev_err(&client->dev, "failed : add sysfs entries\n");
G.Shark Jeong0f598582012-10-04 17:12:55 -0700363 goto err_bled_mode;
364 }
365
366 /* flash */
367 pchip->cdev_flash.name = "lm3639_flash";
368 pchip->cdev_flash.max_brightness = 16;
369 pchip->cdev_flash.brightness_set = lm3639_flash_brightness_set;
370 ret = led_classdev_register((struct device *)
371 &client->dev, &pchip->cdev_flash);
372 if (ret < 0) {
373 dev_err(&client->dev, "fail : flash register\n");
G.Shark Jeong0f598582012-10-04 17:12:55 -0700374 goto err_flash;
375 }
376
377 /* torch */
378 pchip->cdev_torch.name = "lm3639_torch";
379 pchip->cdev_torch.max_brightness = 8;
380 pchip->cdev_torch.brightness_set = lm3639_torch_brightness_set;
381 ret = led_classdev_register((struct device *)
382 &client->dev, &pchip->cdev_torch);
383 if (ret < 0) {
384 dev_err(&client->dev, "fail : torch register\n");
G.Shark Jeong0f598582012-10-04 17:12:55 -0700385 goto err_torch;
386 }
387
388 return 0;
389
390err_torch:
391 led_classdev_unregister(&pchip->cdev_flash);
392err_flash:
393 device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode);
394err_bled_mode:
395 backlight_device_unregister(pchip->bled);
396err_out:
397 return ret;
398}
399
Bill Pemberton7e4b9d02012-11-19 13:26:34 -0500400static int lm3639_remove(struct i2c_client *client)
G.Shark Jeong0f598582012-10-04 17:12:55 -0700401{
402 struct lm3639_chip_data *pchip = i2c_get_clientdata(client);
403
404 regmap_write(pchip->regmap, REG_ENABLE, 0x00);
405
406 if (&pchip->cdev_torch)
407 led_classdev_unregister(&pchip->cdev_torch);
408 if (&pchip->cdev_flash)
409 led_classdev_unregister(&pchip->cdev_flash);
410 if (pchip->bled) {
411 device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode);
412 backlight_device_unregister(pchip->bled);
413 }
414 return 0;
415}
416
417static const struct i2c_device_id lm3639_id[] = {
418 {LM3639_NAME, 0},
419 {}
420};
421
422MODULE_DEVICE_TABLE(i2c, lm3639_id);
423static struct i2c_driver lm3639_i2c_driver = {
424 .driver = {
425 .name = LM3639_NAME,
426 },
427 .probe = lm3639_probe,
Bill Pembertond1723fa2012-11-19 13:21:09 -0500428 .remove = lm3639_remove,
G.Shark Jeong0f598582012-10-04 17:12:55 -0700429 .id_table = lm3639_id,
430};
431
432module_i2c_driver(lm3639_i2c_driver);
433
434MODULE_DESCRIPTION("Texas Instruments Backlight+Flash LED driver for LM3639");
435MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>");
436MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>");
437MODULE_LICENSE("GPL v2");