]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/video/backlight/lm3630_bl.c
Merge tag 'mvebu' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[karo-tx-linux.git] / drivers / video / backlight / lm3630_bl.c
1 /*
2 * Simple driver for Texas Instruments LM3630 Backlight 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/backlight.h>
14 #include <linux/err.h>
15 #include <linux/delay.h>
16 #include <linux/uaccess.h>
17 #include <linux/interrupt.h>
18 #include <linux/regmap.h>
19 #include <linux/platform_data/lm3630_bl.h>
20
21 #define REG_CTRL        0x00
22 #define REG_CONFIG      0x01
23 #define REG_BRT_A       0x03
24 #define REG_BRT_B       0x04
25 #define REG_INT_STATUS  0x09
26 #define REG_INT_EN      0x0A
27 #define REG_FAULT       0x0B
28 #define REG_PWM_OUTLOW  0x12
29 #define REG_PWM_OUTHIGH 0x13
30 #define REG_MAX         0x1F
31
32 #define INT_DEBOUNCE_MSEC       10
33
34 enum lm3630_leds {
35         BLED_ALL = 0,
36         BLED_1,
37         BLED_2
38 };
39
40 static const char *bled_name[] = {
41         [BLED_ALL] = "lm3630_bled",     /*Bank1 controls all string */
42         [BLED_1] = "lm3630_bled1",      /*Bank1 controls bled1 */
43         [BLED_2] = "lm3630_bled2",      /*Bank1 or 2 controls bled2 */
44 };
45
46 struct lm3630_chip_data {
47         struct device *dev;
48         struct delayed_work work;
49         int irq;
50         struct workqueue_struct *irqthread;
51         struct lm3630_platform_data *pdata;
52         struct backlight_device *bled1;
53         struct backlight_device *bled2;
54         struct regmap *regmap;
55 };
56
57 /* initialize chip */
58 static int lm3630_chip_init(struct lm3630_chip_data *pchip)
59 {
60         int ret;
61         unsigned int reg_val;
62         struct lm3630_platform_data *pdata = pchip->pdata;
63
64         /*pwm control */
65         reg_val = ((pdata->pwm_active & 0x01) << 2) | (pdata->pwm_ctrl & 0x03);
66         ret = regmap_update_bits(pchip->regmap, REG_CONFIG, 0x07, reg_val);
67         if (ret < 0)
68                 goto out;
69
70         /* bank control */
71         reg_val = ((pdata->bank_b_ctrl & 0x01) << 1) |
72                         (pdata->bank_a_ctrl & 0x07);
73         ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x07, reg_val);
74         if (ret < 0)
75                 goto out;
76
77         ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x80, 0x00);
78         if (ret < 0)
79                 goto out;
80
81         /* set initial brightness */
82         if (pdata->bank_a_ctrl != BANK_A_CTRL_DISABLE) {
83                 ret = regmap_write(pchip->regmap,
84                                    REG_BRT_A, pdata->init_brt_led1);
85                 if (ret < 0)
86                         goto out;
87         }
88
89         if (pdata->bank_b_ctrl != BANK_B_CTRL_DISABLE) {
90                 ret = regmap_write(pchip->regmap,
91                                    REG_BRT_B, pdata->init_brt_led2);
92                 if (ret < 0)
93                         goto out;
94         }
95         return ret;
96
97 out:
98         dev_err(pchip->dev, "i2c failed to access register\n");
99         return ret;
100 }
101
102 /* interrupt handling */
103 static void lm3630_delayed_func(struct work_struct *work)
104 {
105         int ret;
106         unsigned int reg_val;
107         struct lm3630_chip_data *pchip;
108
109         pchip = container_of(work, struct lm3630_chip_data, work.work);
110
111         ret = regmap_read(pchip->regmap, REG_INT_STATUS, &reg_val);
112         if (ret < 0) {
113                 dev_err(pchip->dev,
114                         "i2c failed to access REG_INT_STATUS Register\n");
115                 return;
116         }
117
118         dev_info(pchip->dev, "REG_INT_STATUS Register is 0x%x\n", reg_val);
119 }
120
121 static irqreturn_t lm3630_isr_func(int irq, void *chip)
122 {
123         int ret;
124         struct lm3630_chip_data *pchip = chip;
125         unsigned long delay = msecs_to_jiffies(INT_DEBOUNCE_MSEC);
126
127         queue_delayed_work(pchip->irqthread, &pchip->work, delay);
128
129         ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x80, 0x00);
130         if (ret < 0)
131                 goto out;
132
133         return IRQ_HANDLED;
134 out:
135         dev_err(pchip->dev, "i2c failed to access register\n");
136         return IRQ_HANDLED;
137 }
138
139 static int lm3630_intr_config(struct lm3630_chip_data *pchip)
140 {
141         INIT_DELAYED_WORK(&pchip->work, lm3630_delayed_func);
142         pchip->irqthread = create_singlethread_workqueue("lm3630-irqthd");
143         if (!pchip->irqthread) {
144                 dev_err(pchip->dev, "create irq thread fail...\n");
145                 return -1;
146         }
147         if (request_threaded_irq
148             (pchip->irq, NULL, lm3630_isr_func,
149              IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "lm3630_irq", pchip)) {
150                 dev_err(pchip->dev, "request threaded irq fail..\n");
151                 return -1;
152         }
153         return 0;
154 }
155
156 static bool
157 set_intensity(struct backlight_device *bl, struct lm3630_chip_data *pchip)
158 {
159         if (!pchip->pdata->pwm_set_intensity)
160                 return false;
161         pchip->pdata->pwm_set_intensity(bl->props.brightness - 1,
162                                         pchip->pdata->pwm_period);
163         return true;
164 }
165
166 /* update and get brightness */
167 static int lm3630_bank_a_update_status(struct backlight_device *bl)
168 {
169         int ret;
170         struct lm3630_chip_data *pchip = bl_get_data(bl);
171         enum lm3630_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl;
172
173         /* brightness 0 means disable */
174         if (!bl->props.brightness) {
175                 ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x04, 0x00);
176                 if (ret < 0)
177                         goto out;
178                 return bl->props.brightness;
179         }
180
181         /* pwm control */
182         if (pwm_ctrl == PWM_CTRL_BANK_A || pwm_ctrl == PWM_CTRL_BANK_ALL) {
183                 if (!set_intensity(bl, pchip))
184                         dev_err(pchip->dev, "No pwm control func. in plat-data\n");
185         } else {
186
187                 /* i2c control */
188                 ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x80, 0x00);
189                 if (ret < 0)
190                         goto out;
191                 mdelay(1);
192                 ret = regmap_write(pchip->regmap,
193                                    REG_BRT_A, bl->props.brightness - 1);
194                 if (ret < 0)
195                         goto out;
196         }
197         return bl->props.brightness;
198 out:
199         dev_err(pchip->dev, "i2c failed to access REG_CTRL\n");
200         return bl->props.brightness;
201 }
202
203 static int lm3630_bank_a_get_brightness(struct backlight_device *bl)
204 {
205         unsigned int reg_val;
206         int brightness, ret;
207         struct lm3630_chip_data *pchip = bl_get_data(bl);
208         enum lm3630_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl;
209
210         if (pwm_ctrl == PWM_CTRL_BANK_A || pwm_ctrl == PWM_CTRL_BANK_ALL) {
211                 ret = regmap_read(pchip->regmap, REG_PWM_OUTHIGH, &reg_val);
212                 if (ret < 0)
213                         goto out;
214                 brightness = reg_val & 0x01;
215                 ret = regmap_read(pchip->regmap, REG_PWM_OUTLOW, &reg_val);
216                 if (ret < 0)
217                         goto out;
218                 brightness = ((brightness << 8) | reg_val) + 1;
219         } else {
220                 ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x80, 0x00);
221                 if (ret < 0)
222                         goto out;
223                 mdelay(1);
224                 ret = regmap_read(pchip->regmap, REG_BRT_A, &reg_val);
225                 if (ret < 0)
226                         goto out;
227                 brightness = reg_val + 1;
228         }
229         bl->props.brightness = brightness;
230         return bl->props.brightness;
231 out:
232         dev_err(pchip->dev, "i2c failed to access register\n");
233         return 0;
234 }
235
236 static const struct backlight_ops lm3630_bank_a_ops = {
237         .options = BL_CORE_SUSPENDRESUME,
238         .update_status = lm3630_bank_a_update_status,
239         .get_brightness = lm3630_bank_a_get_brightness,
240 };
241
242 static int lm3630_bank_b_update_status(struct backlight_device *bl)
243 {
244         int ret;
245         struct lm3630_chip_data *pchip = bl_get_data(bl);
246         enum lm3630_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl;
247
248         if (pwm_ctrl == PWM_CTRL_BANK_B || pwm_ctrl == PWM_CTRL_BANK_ALL) {
249                 if (!set_intensity(bl, pchip))
250                         dev_err(pchip->dev,
251                                 "no pwm control func. in plat-data\n");
252         } else {
253                 ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x80, 0x00);
254                 if (ret < 0)
255                         goto out;
256                 mdelay(1);
257                 ret = regmap_write(pchip->regmap,
258                                    REG_BRT_B, bl->props.brightness - 1);
259         }
260         return bl->props.brightness;
261 out:
262         dev_err(pchip->dev, "i2c failed to access register\n");
263         return bl->props.brightness;
264 }
265
266 static int lm3630_bank_b_get_brightness(struct backlight_device *bl)
267 {
268         unsigned int reg_val;
269         int brightness, ret;
270         struct lm3630_chip_data *pchip = bl_get_data(bl);
271         enum lm3630_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl;
272
273         if (pwm_ctrl == PWM_CTRL_BANK_B || pwm_ctrl == PWM_CTRL_BANK_ALL) {
274                 ret = regmap_read(pchip->regmap, REG_PWM_OUTHIGH, &reg_val);
275                 if (ret < 0)
276                         goto out;
277                 brightness = reg_val & 0x01;
278                 ret = regmap_read(pchip->regmap, REG_PWM_OUTLOW, &reg_val);
279                 if (ret < 0)
280                         goto out;
281                 brightness = ((brightness << 8) | reg_val) + 1;
282         } else {
283                 ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x80, 0x00);
284                 if (ret < 0)
285                         goto out;
286                 mdelay(1);
287                 ret = regmap_read(pchip->regmap, REG_BRT_B, &reg_val);
288                 if (ret < 0)
289                         goto out;
290                 brightness = reg_val + 1;
291         }
292         bl->props.brightness = brightness;
293
294         return bl->props.brightness;
295 out:
296         dev_err(pchip->dev, "i2c failed to access register\n");
297         return bl->props.brightness;
298 }
299
300 static const struct backlight_ops lm3630_bank_b_ops = {
301         .options = BL_CORE_SUSPENDRESUME,
302         .update_status = lm3630_bank_b_update_status,
303         .get_brightness = lm3630_bank_b_get_brightness,
304 };
305
306 static int lm3630_backlight_register(struct lm3630_chip_data *pchip,
307                                      enum lm3630_leds ledno)
308 {
309         const char *name = bled_name[ledno];
310         struct backlight_properties props;
311         struct lm3630_platform_data *pdata = pchip->pdata;
312
313         props.type = BACKLIGHT_RAW;
314         switch (ledno) {
315         case BLED_1:
316         case BLED_ALL:
317                 props.brightness = pdata->init_brt_led1;
318                 props.max_brightness = pdata->max_brt_led1;
319                 pchip->bled1 =
320                     backlight_device_register(name, pchip->dev, pchip,
321                                               &lm3630_bank_a_ops, &props);
322                 if (IS_ERR(pchip->bled1))
323                         return -EIO;
324                 break;
325         case BLED_2:
326                 props.brightness = pdata->init_brt_led2;
327                 props.max_brightness = pdata->max_brt_led2;
328                 pchip->bled2 =
329                     backlight_device_register(name, pchip->dev, pchip,
330                                               &lm3630_bank_b_ops, &props);
331                 if (IS_ERR(pchip->bled2))
332                         return -EIO;
333                 break;
334         }
335         return 0;
336 }
337
338 static void lm3630_backlight_unregister(struct lm3630_chip_data *pchip)
339 {
340         if (pchip->bled1)
341                 backlight_device_unregister(pchip->bled1);
342         if (pchip->bled2)
343                 backlight_device_unregister(pchip->bled2);
344 }
345
346 static const struct regmap_config lm3630_regmap = {
347         .reg_bits = 8,
348         .val_bits = 8,
349         .max_register = REG_MAX,
350 };
351
352 static int lm3630_probe(struct i2c_client *client,
353                                   const struct i2c_device_id *id)
354 {
355         struct lm3630_platform_data *pdata = client->dev.platform_data;
356         struct lm3630_chip_data *pchip;
357         int ret;
358
359         if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
360                 dev_err(&client->dev, "fail : i2c functionality check...\n");
361                 return -EOPNOTSUPP;
362         }
363
364         if (pdata == NULL) {
365                 dev_err(&client->dev, "fail : no platform data.\n");
366                 return -ENODATA;
367         }
368
369         pchip = devm_kzalloc(&client->dev, sizeof(struct lm3630_chip_data),
370                              GFP_KERNEL);
371         if (!pchip)
372                 return -ENOMEM;
373         pchip->pdata = pdata;
374         pchip->dev = &client->dev;
375
376         pchip->regmap = devm_regmap_init_i2c(client, &lm3630_regmap);
377         if (IS_ERR(pchip->regmap)) {
378                 ret = PTR_ERR(pchip->regmap);
379                 dev_err(&client->dev, "fail : allocate register map: %d\n",
380                         ret);
381                 return ret;
382         }
383         i2c_set_clientdata(client, pchip);
384
385         /* chip initialize */
386         ret = lm3630_chip_init(pchip);
387         if (ret < 0) {
388                 dev_err(&client->dev, "fail : init chip\n");
389                 goto err_chip_init;
390         }
391
392         switch (pdata->bank_a_ctrl) {
393         case BANK_A_CTRL_ALL:
394                 ret = lm3630_backlight_register(pchip, BLED_ALL);
395                 pdata->bank_b_ctrl = BANK_B_CTRL_DISABLE;
396                 break;
397         case BANK_A_CTRL_LED1:
398                 ret = lm3630_backlight_register(pchip, BLED_1);
399                 break;
400         case BANK_A_CTRL_LED2:
401                 ret = lm3630_backlight_register(pchip, BLED_2);
402                 pdata->bank_b_ctrl = BANK_B_CTRL_DISABLE;
403                 break;
404         default:
405                 break;
406         }
407
408         if (ret < 0)
409                 goto err_bl_reg;
410
411         if (pdata->bank_b_ctrl && pchip->bled2 == NULL) {
412                 ret = lm3630_backlight_register(pchip, BLED_2);
413                 if (ret < 0)
414                         goto err_bl_reg;
415         }
416
417         /* interrupt enable  : irq 0 is not allowed for lm3630 */
418         pchip->irq = client->irq;
419         if (pchip->irq)
420                 lm3630_intr_config(pchip);
421
422         dev_info(&client->dev, "LM3630 backlight register OK.\n");
423         return 0;
424
425 err_bl_reg:
426         dev_err(&client->dev, "fail : backlight register.\n");
427         lm3630_backlight_unregister(pchip);
428 err_chip_init:
429         return ret;
430 }
431
432 static int lm3630_remove(struct i2c_client *client)
433 {
434         int ret;
435         struct lm3630_chip_data *pchip = i2c_get_clientdata(client);
436
437         ret = regmap_write(pchip->regmap, REG_BRT_A, 0);
438         if (ret < 0)
439                 dev_err(pchip->dev, "i2c failed to access register\n");
440
441         ret = regmap_write(pchip->regmap, REG_BRT_B, 0);
442         if (ret < 0)
443                 dev_err(pchip->dev, "i2c failed to access register\n");
444
445         lm3630_backlight_unregister(pchip);
446         if (pchip->irq) {
447                 free_irq(pchip->irq, pchip);
448                 flush_workqueue(pchip->irqthread);
449                 destroy_workqueue(pchip->irqthread);
450         }
451         return 0;
452 }
453
454 static const struct i2c_device_id lm3630_id[] = {
455         {LM3630_NAME, 0},
456         {}
457 };
458
459 MODULE_DEVICE_TABLE(i2c, lm3630_id);
460
461 static struct i2c_driver lm3630_i2c_driver = {
462         .driver = {
463                    .name = LM3630_NAME,
464                    },
465         .probe = lm3630_probe,
466         .remove = lm3630_remove,
467         .id_table = lm3630_id,
468 };
469
470 module_i2c_driver(lm3630_i2c_driver);
471
472 MODULE_DESCRIPTION("Texas Instruments Backlight driver for LM3630");
473 MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>");
474 MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>");
475 MODULE_LICENSE("GPL v2");