1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Simple driver for Texas Instruments LM3630A Backlight driver chip |
4 | * Copyright (C) 2012 Texas Instruments |
5 | */ |
6 | #include <linux/module.h> |
7 | #include <linux/slab.h> |
8 | #include <linux/i2c.h> |
9 | #include <linux/backlight.h> |
10 | #include <linux/err.h> |
11 | #include <linux/delay.h> |
12 | #include <linux/uaccess.h> |
13 | #include <linux/interrupt.h> |
14 | #include <linux/regmap.h> |
15 | #include <linux/gpio/consumer.h> |
16 | #include <linux/pwm.h> |
17 | #include <linux/platform_data/lm3630a_bl.h> |
18 | |
19 | #define REG_CTRL 0x00 |
20 | #define REG_BOOST 0x02 |
21 | #define REG_CONFIG 0x01 |
22 | #define REG_BRT_A 0x03 |
23 | #define REG_BRT_B 0x04 |
24 | #define REG_I_A 0x05 |
25 | #define REG_I_B 0x06 |
26 | #define REG_INT_STATUS 0x09 |
27 | #define REG_INT_EN 0x0A |
28 | #define REG_FAULT 0x0B |
29 | #define REG_PWM_OUTLOW 0x12 |
30 | #define REG_PWM_OUTHIGH 0x13 |
31 | #define REG_FILTER_STRENGTH 0x50 |
32 | #define REG_MAX 0x50 |
33 | |
34 | #define INT_DEBOUNCE_MSEC 10 |
35 | |
36 | #define LM3630A_BANK_0 0 |
37 | #define LM3630A_BANK_1 1 |
38 | |
39 | #define LM3630A_NUM_SINKS 2 |
40 | #define LM3630A_SINK_0 0 |
41 | #define LM3630A_SINK_1 1 |
42 | |
43 | struct lm3630a_chip { |
44 | struct device *dev; |
45 | struct delayed_work work; |
46 | |
47 | int irq; |
48 | struct workqueue_struct *irqthread; |
49 | struct lm3630a_platform_data *pdata; |
50 | struct backlight_device *bleda; |
51 | struct backlight_device *bledb; |
52 | struct gpio_desc *enable_gpio; |
53 | struct regmap *regmap; |
54 | struct pwm_device *pwmd; |
55 | struct pwm_state pwmd_state; |
56 | }; |
57 | |
58 | /* i2c access */ |
59 | static int lm3630a_read(struct lm3630a_chip *pchip, unsigned int reg) |
60 | { |
61 | int rval; |
62 | unsigned int reg_val; |
63 | |
64 | rval = regmap_read(map: pchip->regmap, reg, val: ®_val); |
65 | if (rval < 0) |
66 | return rval; |
67 | return reg_val & 0xFF; |
68 | } |
69 | |
70 | static int lm3630a_write(struct lm3630a_chip *pchip, |
71 | unsigned int reg, unsigned int data) |
72 | { |
73 | return regmap_write(map: pchip->regmap, reg, val: data); |
74 | } |
75 | |
76 | static int lm3630a_update(struct lm3630a_chip *pchip, |
77 | unsigned int reg, unsigned int mask, |
78 | unsigned int data) |
79 | { |
80 | return regmap_update_bits(map: pchip->regmap, reg, mask, val: data); |
81 | } |
82 | |
83 | /* initialize chip */ |
84 | static int lm3630a_chip_init(struct lm3630a_chip *pchip) |
85 | { |
86 | int rval; |
87 | struct lm3630a_platform_data *pdata = pchip->pdata; |
88 | |
89 | usleep_range(min: 1000, max: 2000); |
90 | /* set Filter Strength Register */ |
91 | rval = lm3630a_write(pchip, REG_FILTER_STRENGTH, data: 0x03); |
92 | /* set Cofig. register */ |
93 | rval |= lm3630a_update(pchip, REG_CONFIG, mask: 0x07, data: pdata->pwm_ctrl); |
94 | /* set boost control */ |
95 | rval |= lm3630a_write(pchip, REG_BOOST, data: 0x38); |
96 | /* set current A */ |
97 | rval |= lm3630a_update(pchip, REG_I_A, mask: 0x1F, data: 0x1F); |
98 | /* set current B */ |
99 | rval |= lm3630a_write(pchip, REG_I_B, data: 0x1F); |
100 | /* set control */ |
101 | rval |= lm3630a_update(pchip, REG_CTRL, mask: 0x14, data: pdata->leda_ctrl); |
102 | rval |= lm3630a_update(pchip, REG_CTRL, mask: 0x0B, data: pdata->ledb_ctrl); |
103 | usleep_range(min: 1000, max: 2000); |
104 | /* set brightness A and B */ |
105 | rval |= lm3630a_write(pchip, REG_BRT_A, data: pdata->leda_init_brt); |
106 | rval |= lm3630a_write(pchip, REG_BRT_B, data: pdata->ledb_init_brt); |
107 | |
108 | if (rval < 0) |
109 | dev_err(pchip->dev, "i2c failed to access register\n" ); |
110 | return rval; |
111 | } |
112 | |
113 | /* interrupt handling */ |
114 | static void lm3630a_delayed_func(struct work_struct *work) |
115 | { |
116 | int rval; |
117 | struct lm3630a_chip *pchip; |
118 | |
119 | pchip = container_of(work, struct lm3630a_chip, work.work); |
120 | |
121 | rval = lm3630a_read(pchip, REG_INT_STATUS); |
122 | if (rval < 0) { |
123 | dev_err(pchip->dev, |
124 | "i2c failed to access REG_INT_STATUS Register\n" ); |
125 | return; |
126 | } |
127 | |
128 | dev_info(pchip->dev, "REG_INT_STATUS Register is 0x%x\n" , rval); |
129 | } |
130 | |
131 | static irqreturn_t lm3630a_isr_func(int irq, void *chip) |
132 | { |
133 | int rval; |
134 | struct lm3630a_chip *pchip = chip; |
135 | unsigned long delay = msecs_to_jiffies(INT_DEBOUNCE_MSEC); |
136 | |
137 | queue_delayed_work(wq: pchip->irqthread, dwork: &pchip->work, delay); |
138 | |
139 | rval = lm3630a_update(pchip, REG_CTRL, mask: 0x80, data: 0x00); |
140 | if (rval < 0) { |
141 | dev_err(pchip->dev, "i2c failed to access register\n" ); |
142 | return IRQ_NONE; |
143 | } |
144 | return IRQ_HANDLED; |
145 | } |
146 | |
147 | static int lm3630a_intr_config(struct lm3630a_chip *pchip) |
148 | { |
149 | int rval; |
150 | |
151 | rval = lm3630a_write(pchip, REG_INT_EN, data: 0x87); |
152 | if (rval < 0) |
153 | return rval; |
154 | |
155 | INIT_DELAYED_WORK(&pchip->work, lm3630a_delayed_func); |
156 | pchip->irqthread = create_singlethread_workqueue("lm3630a-irqthd" ); |
157 | if (!pchip->irqthread) { |
158 | dev_err(pchip->dev, "create irq thread fail\n" ); |
159 | return -ENOMEM; |
160 | } |
161 | if (request_threaded_irq |
162 | (irq: pchip->irq, NULL, thread_fn: lm3630a_isr_func, |
163 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, name: "lm3630a_irq" , dev: pchip)) { |
164 | dev_err(pchip->dev, "request threaded irq fail\n" ); |
165 | destroy_workqueue(wq: pchip->irqthread); |
166 | return -ENOMEM; |
167 | } |
168 | return rval; |
169 | } |
170 | |
171 | static int lm3630a_pwm_ctrl(struct lm3630a_chip *pchip, int br, int br_max) |
172 | { |
173 | int err; |
174 | |
175 | pchip->pwmd_state.period = pchip->pdata->pwm_period; |
176 | |
177 | err = pwm_set_relative_duty_cycle(state: &pchip->pwmd_state, duty_cycle: br, scale: br_max); |
178 | if (err) |
179 | return err; |
180 | |
181 | pchip->pwmd_state.enabled = pchip->pwmd_state.duty_cycle ? true : false; |
182 | |
183 | return pwm_apply_might_sleep(pwm: pchip->pwmd, state: &pchip->pwmd_state); |
184 | } |
185 | |
186 | /* update and get brightness */ |
187 | static int lm3630a_bank_a_update_status(struct backlight_device *bl) |
188 | { |
189 | int ret; |
190 | struct lm3630a_chip *pchip = bl_get_data(bl_dev: bl); |
191 | enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; |
192 | int brightness = backlight_get_brightness(bd: bl); |
193 | |
194 | /* pwm control */ |
195 | if ((pwm_ctrl & LM3630A_PWM_BANK_A) != 0) |
196 | return lm3630a_pwm_ctrl(pchip, br: brightness, |
197 | br_max: bl->props.max_brightness); |
198 | |
199 | /* disable sleep */ |
200 | ret = lm3630a_update(pchip, REG_CTRL, mask: 0x80, data: 0x00); |
201 | if (ret < 0) |
202 | goto out_i2c_err; |
203 | usleep_range(min: 1000, max: 2000); |
204 | /* minimum brightness is 0x04 */ |
205 | ret = lm3630a_write(pchip, REG_BRT_A, data: brightness); |
206 | |
207 | if (brightness < 0x4) |
208 | /* turn the string off */ |
209 | ret |= lm3630a_update(pchip, REG_CTRL, mask: LM3630A_LEDA_ENABLE, data: 0); |
210 | else |
211 | ret |= lm3630a_update(pchip, REG_CTRL, |
212 | mask: LM3630A_LEDA_ENABLE, data: LM3630A_LEDA_ENABLE); |
213 | if (ret < 0) |
214 | goto out_i2c_err; |
215 | return 0; |
216 | |
217 | out_i2c_err: |
218 | dev_err(pchip->dev, "i2c failed to access (%pe)\n" , ERR_PTR(ret)); |
219 | return ret; |
220 | } |
221 | |
222 | static int lm3630a_bank_a_get_brightness(struct backlight_device *bl) |
223 | { |
224 | int brightness, rval; |
225 | struct lm3630a_chip *pchip = bl_get_data(bl_dev: bl); |
226 | enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; |
227 | |
228 | if ((pwm_ctrl & LM3630A_PWM_BANK_A) != 0) { |
229 | rval = lm3630a_read(pchip, REG_PWM_OUTHIGH); |
230 | if (rval < 0) |
231 | goto out_i2c_err; |
232 | brightness = (rval & 0x01) << 8; |
233 | rval = lm3630a_read(pchip, REG_PWM_OUTLOW); |
234 | if (rval < 0) |
235 | goto out_i2c_err; |
236 | brightness |= rval; |
237 | return brightness; |
238 | } |
239 | |
240 | /* disable sleep */ |
241 | rval = lm3630a_update(pchip, REG_CTRL, mask: 0x80, data: 0x00); |
242 | if (rval < 0) |
243 | goto out_i2c_err; |
244 | usleep_range(min: 1000, max: 2000); |
245 | rval = lm3630a_read(pchip, REG_BRT_A); |
246 | if (rval < 0) |
247 | goto out_i2c_err; |
248 | return rval; |
249 | |
250 | out_i2c_err: |
251 | dev_err(pchip->dev, "i2c failed to access register\n" ); |
252 | return 0; |
253 | } |
254 | |
255 | static const struct backlight_ops lm3630a_bank_a_ops = { |
256 | .options = BL_CORE_SUSPENDRESUME, |
257 | .update_status = lm3630a_bank_a_update_status, |
258 | .get_brightness = lm3630a_bank_a_get_brightness, |
259 | }; |
260 | |
261 | /* update and get brightness */ |
262 | static int lm3630a_bank_b_update_status(struct backlight_device *bl) |
263 | { |
264 | int ret; |
265 | struct lm3630a_chip *pchip = bl_get_data(bl_dev: bl); |
266 | enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; |
267 | int brightness = backlight_get_brightness(bd: bl); |
268 | |
269 | /* pwm control */ |
270 | if ((pwm_ctrl & LM3630A_PWM_BANK_B) != 0) |
271 | return lm3630a_pwm_ctrl(pchip, br: brightness, |
272 | br_max: bl->props.max_brightness); |
273 | |
274 | /* disable sleep */ |
275 | ret = lm3630a_update(pchip, REG_CTRL, mask: 0x80, data: 0x00); |
276 | if (ret < 0) |
277 | goto out_i2c_err; |
278 | usleep_range(min: 1000, max: 2000); |
279 | /* minimum brightness is 0x04 */ |
280 | ret = lm3630a_write(pchip, REG_BRT_B, data: brightness); |
281 | |
282 | if (brightness < 0x4) |
283 | /* turn the string off */ |
284 | ret |= lm3630a_update(pchip, REG_CTRL, mask: LM3630A_LEDB_ENABLE, data: 0); |
285 | else |
286 | ret |= lm3630a_update(pchip, REG_CTRL, |
287 | mask: LM3630A_LEDB_ENABLE, data: LM3630A_LEDB_ENABLE); |
288 | if (ret < 0) |
289 | goto out_i2c_err; |
290 | return 0; |
291 | |
292 | out_i2c_err: |
293 | dev_err(pchip->dev, "i2c failed to access (%pe)\n" , ERR_PTR(ret)); |
294 | return ret; |
295 | } |
296 | |
297 | static int lm3630a_bank_b_get_brightness(struct backlight_device *bl) |
298 | { |
299 | int brightness, rval; |
300 | struct lm3630a_chip *pchip = bl_get_data(bl_dev: bl); |
301 | enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; |
302 | |
303 | if ((pwm_ctrl & LM3630A_PWM_BANK_B) != 0) { |
304 | rval = lm3630a_read(pchip, REG_PWM_OUTHIGH); |
305 | if (rval < 0) |
306 | goto out_i2c_err; |
307 | brightness = (rval & 0x01) << 8; |
308 | rval = lm3630a_read(pchip, REG_PWM_OUTLOW); |
309 | if (rval < 0) |
310 | goto out_i2c_err; |
311 | brightness |= rval; |
312 | return brightness; |
313 | } |
314 | |
315 | /* disable sleep */ |
316 | rval = lm3630a_update(pchip, REG_CTRL, mask: 0x80, data: 0x00); |
317 | if (rval < 0) |
318 | goto out_i2c_err; |
319 | usleep_range(min: 1000, max: 2000); |
320 | rval = lm3630a_read(pchip, REG_BRT_B); |
321 | if (rval < 0) |
322 | goto out_i2c_err; |
323 | return rval; |
324 | |
325 | out_i2c_err: |
326 | dev_err(pchip->dev, "i2c failed to access register\n" ); |
327 | return 0; |
328 | } |
329 | |
330 | static const struct backlight_ops lm3630a_bank_b_ops = { |
331 | .options = BL_CORE_SUSPENDRESUME, |
332 | .update_status = lm3630a_bank_b_update_status, |
333 | .get_brightness = lm3630a_bank_b_get_brightness, |
334 | }; |
335 | |
336 | static int lm3630a_backlight_register(struct lm3630a_chip *pchip) |
337 | { |
338 | struct lm3630a_platform_data *pdata = pchip->pdata; |
339 | struct backlight_properties props; |
340 | const char *label; |
341 | |
342 | memset(&props, 0, sizeof(struct backlight_properties)); |
343 | props.type = BACKLIGHT_RAW; |
344 | if (pdata->leda_ctrl != LM3630A_LEDA_DISABLE) { |
345 | props.brightness = pdata->leda_init_brt; |
346 | props.max_brightness = pdata->leda_max_brt; |
347 | label = pdata->leda_label ? pdata->leda_label : "lm3630a_leda" ; |
348 | pchip->bleda = |
349 | devm_backlight_device_register(dev: pchip->dev, name: label, |
350 | parent: pchip->dev, devdata: pchip, |
351 | ops: &lm3630a_bank_a_ops, props: &props); |
352 | if (IS_ERR(ptr: pchip->bleda)) |
353 | return PTR_ERR(ptr: pchip->bleda); |
354 | } |
355 | |
356 | if ((pdata->ledb_ctrl != LM3630A_LEDB_DISABLE) && |
357 | (pdata->ledb_ctrl != LM3630A_LEDB_ON_A)) { |
358 | props.brightness = pdata->ledb_init_brt; |
359 | props.max_brightness = pdata->ledb_max_brt; |
360 | label = pdata->ledb_label ? pdata->ledb_label : "lm3630a_ledb" ; |
361 | pchip->bledb = |
362 | devm_backlight_device_register(dev: pchip->dev, name: label, |
363 | parent: pchip->dev, devdata: pchip, |
364 | ops: &lm3630a_bank_b_ops, props: &props); |
365 | if (IS_ERR(ptr: pchip->bledb)) |
366 | return PTR_ERR(ptr: pchip->bledb); |
367 | } |
368 | return 0; |
369 | } |
370 | |
371 | static const struct regmap_config lm3630a_regmap = { |
372 | .reg_bits = 8, |
373 | .val_bits = 8, |
374 | .max_register = REG_MAX, |
375 | }; |
376 | |
377 | static int lm3630a_parse_led_sources(struct fwnode_handle *node, |
378 | int default_led_sources) |
379 | { |
380 | u32 sources[LM3630A_NUM_SINKS]; |
381 | int ret, num_sources, i; |
382 | |
383 | num_sources = fwnode_property_count_u32(fwnode: node, propname: "led-sources" ); |
384 | if (num_sources < 0) |
385 | return default_led_sources; |
386 | else if (num_sources > ARRAY_SIZE(sources)) |
387 | return -EINVAL; |
388 | |
389 | ret = fwnode_property_read_u32_array(fwnode: node, propname: "led-sources" , val: sources, |
390 | nval: num_sources); |
391 | if (ret) |
392 | return ret; |
393 | |
394 | for (i = 0; i < num_sources; i++) { |
395 | if (sources[i] != LM3630A_SINK_0 && sources[i] != LM3630A_SINK_1) |
396 | return -EINVAL; |
397 | |
398 | ret |= BIT(sources[i]); |
399 | } |
400 | |
401 | return ret; |
402 | } |
403 | |
404 | static int lm3630a_parse_bank(struct lm3630a_platform_data *pdata, |
405 | struct fwnode_handle *node, int *seen_led_sources) |
406 | { |
407 | int led_sources, ret; |
408 | const char *label; |
409 | u32 bank, val; |
410 | bool linear; |
411 | |
412 | ret = fwnode_property_read_u32(fwnode: node, propname: "reg" , val: &bank); |
413 | if (ret) |
414 | return ret; |
415 | |
416 | if (bank != LM3630A_BANK_0 && bank != LM3630A_BANK_1) |
417 | return -EINVAL; |
418 | |
419 | led_sources = lm3630a_parse_led_sources(node, BIT(bank)); |
420 | if (led_sources < 0) |
421 | return led_sources; |
422 | |
423 | if (*seen_led_sources & led_sources) |
424 | return -EINVAL; |
425 | |
426 | *seen_led_sources |= led_sources; |
427 | |
428 | linear = fwnode_property_read_bool(fwnode: node, |
429 | propname: "ti,linear-mapping-mode" ); |
430 | if (bank) { |
431 | if (led_sources & BIT(LM3630A_SINK_0) || |
432 | !(led_sources & BIT(LM3630A_SINK_1))) |
433 | return -EINVAL; |
434 | |
435 | pdata->ledb_ctrl = linear ? |
436 | LM3630A_LEDB_ENABLE_LINEAR : |
437 | LM3630A_LEDB_ENABLE; |
438 | } else { |
439 | if (!(led_sources & BIT(LM3630A_SINK_0))) |
440 | return -EINVAL; |
441 | |
442 | pdata->leda_ctrl = linear ? |
443 | LM3630A_LEDA_ENABLE_LINEAR : |
444 | LM3630A_LEDA_ENABLE; |
445 | |
446 | if (led_sources & BIT(LM3630A_SINK_1)) |
447 | pdata->ledb_ctrl = LM3630A_LEDB_ON_A; |
448 | } |
449 | |
450 | ret = fwnode_property_read_string(fwnode: node, propname: "label" , val: &label); |
451 | if (!ret) { |
452 | if (bank) |
453 | pdata->ledb_label = label; |
454 | else |
455 | pdata->leda_label = label; |
456 | } |
457 | |
458 | ret = fwnode_property_read_u32(fwnode: node, propname: "default-brightness" , |
459 | val: &val); |
460 | if (!ret) { |
461 | if (bank) |
462 | pdata->ledb_init_brt = val; |
463 | else |
464 | pdata->leda_init_brt = val; |
465 | } |
466 | |
467 | ret = fwnode_property_read_u32(fwnode: node, propname: "max-brightness" , val: &val); |
468 | if (!ret) { |
469 | if (bank) |
470 | pdata->ledb_max_brt = val; |
471 | else |
472 | pdata->leda_max_brt = val; |
473 | } |
474 | |
475 | return 0; |
476 | } |
477 | |
478 | static int lm3630a_parse_node(struct lm3630a_chip *pchip, |
479 | struct lm3630a_platform_data *pdata) |
480 | { |
481 | int ret = -ENODEV, seen_led_sources = 0; |
482 | struct fwnode_handle *node; |
483 | |
484 | device_for_each_child_node(pchip->dev, node) { |
485 | ret = lm3630a_parse_bank(pdata, node, seen_led_sources: &seen_led_sources); |
486 | if (ret) { |
487 | fwnode_handle_put(fwnode: node); |
488 | return ret; |
489 | } |
490 | } |
491 | |
492 | return ret; |
493 | } |
494 | |
495 | static int lm3630a_probe(struct i2c_client *client) |
496 | { |
497 | struct lm3630a_platform_data *pdata = dev_get_platdata(dev: &client->dev); |
498 | struct lm3630a_chip *pchip; |
499 | int rval; |
500 | |
501 | if (!i2c_check_functionality(adap: client->adapter, I2C_FUNC_I2C)) { |
502 | dev_err(&client->dev, "fail : i2c functionality check\n" ); |
503 | return -EOPNOTSUPP; |
504 | } |
505 | |
506 | pchip = devm_kzalloc(dev: &client->dev, size: sizeof(struct lm3630a_chip), |
507 | GFP_KERNEL); |
508 | if (!pchip) |
509 | return -ENOMEM; |
510 | pchip->dev = &client->dev; |
511 | |
512 | pchip->regmap = devm_regmap_init_i2c(client, &lm3630a_regmap); |
513 | if (IS_ERR(ptr: pchip->regmap)) { |
514 | rval = PTR_ERR(ptr: pchip->regmap); |
515 | dev_err(&client->dev, "fail : allocate reg. map: %d\n" , rval); |
516 | return rval; |
517 | } |
518 | |
519 | i2c_set_clientdata(client, data: pchip); |
520 | if (pdata == NULL) { |
521 | pdata = devm_kzalloc(dev: pchip->dev, |
522 | size: sizeof(struct lm3630a_platform_data), |
523 | GFP_KERNEL); |
524 | if (pdata == NULL) |
525 | return -ENOMEM; |
526 | |
527 | /* default values */ |
528 | pdata->leda_max_brt = LM3630A_MAX_BRIGHTNESS; |
529 | pdata->ledb_max_brt = LM3630A_MAX_BRIGHTNESS; |
530 | pdata->leda_init_brt = LM3630A_MAX_BRIGHTNESS; |
531 | pdata->ledb_init_brt = LM3630A_MAX_BRIGHTNESS; |
532 | |
533 | rval = lm3630a_parse_node(pchip, pdata); |
534 | if (rval) { |
535 | dev_err(&client->dev, "fail : parse node\n" ); |
536 | return rval; |
537 | } |
538 | } |
539 | pchip->pdata = pdata; |
540 | |
541 | pchip->enable_gpio = devm_gpiod_get_optional(dev: &client->dev, con_id: "enable" , |
542 | flags: GPIOD_OUT_HIGH); |
543 | if (IS_ERR(ptr: pchip->enable_gpio)) |
544 | return PTR_ERR(ptr: pchip->enable_gpio); |
545 | |
546 | /* chip initialize */ |
547 | rval = lm3630a_chip_init(pchip); |
548 | if (rval < 0) { |
549 | dev_err(&client->dev, "fail : init chip\n" ); |
550 | return rval; |
551 | } |
552 | /* backlight register */ |
553 | rval = lm3630a_backlight_register(pchip); |
554 | if (rval < 0) { |
555 | dev_err(&client->dev, "fail : backlight register.\n" ); |
556 | return rval; |
557 | } |
558 | /* pwm */ |
559 | if (pdata->pwm_ctrl != LM3630A_PWM_DISABLE) { |
560 | pchip->pwmd = devm_pwm_get(dev: pchip->dev, con_id: "lm3630a-pwm" ); |
561 | if (IS_ERR(ptr: pchip->pwmd)) |
562 | return dev_err_probe(dev: &client->dev, err: PTR_ERR(ptr: pchip->pwmd), |
563 | fmt: "fail : get pwm device\n" ); |
564 | |
565 | pwm_init_state(pwm: pchip->pwmd, state: &pchip->pwmd_state); |
566 | } |
567 | |
568 | /* interrupt enable : irq 0 is not allowed */ |
569 | pchip->irq = client->irq; |
570 | if (pchip->irq) { |
571 | rval = lm3630a_intr_config(pchip); |
572 | if (rval < 0) |
573 | return rval; |
574 | } |
575 | dev_info(&client->dev, "LM3630A backlight register OK.\n" ); |
576 | return 0; |
577 | } |
578 | |
579 | static void lm3630a_remove(struct i2c_client *client) |
580 | { |
581 | int rval; |
582 | struct lm3630a_chip *pchip = i2c_get_clientdata(client); |
583 | |
584 | rval = lm3630a_write(pchip, REG_BRT_A, data: 0); |
585 | if (rval < 0) |
586 | dev_err(pchip->dev, "i2c failed to access register\n" ); |
587 | |
588 | rval = lm3630a_write(pchip, REG_BRT_B, data: 0); |
589 | if (rval < 0) |
590 | dev_err(pchip->dev, "i2c failed to access register\n" ); |
591 | |
592 | if (pchip->irq) { |
593 | free_irq(pchip->irq, pchip); |
594 | destroy_workqueue(wq: pchip->irqthread); |
595 | } |
596 | } |
597 | |
598 | static const struct i2c_device_id lm3630a_id[] = { |
599 | {LM3630A_NAME, 0}, |
600 | {} |
601 | }; |
602 | |
603 | MODULE_DEVICE_TABLE(i2c, lm3630a_id); |
604 | |
605 | static const struct of_device_id lm3630a_match_table[] = { |
606 | { .compatible = "ti,lm3630a" , }, |
607 | { }, |
608 | }; |
609 | |
610 | MODULE_DEVICE_TABLE(of, lm3630a_match_table); |
611 | |
612 | static struct i2c_driver lm3630a_i2c_driver = { |
613 | .driver = { |
614 | .name = LM3630A_NAME, |
615 | .of_match_table = lm3630a_match_table, |
616 | }, |
617 | .probe = lm3630a_probe, |
618 | .remove = lm3630a_remove, |
619 | .id_table = lm3630a_id, |
620 | }; |
621 | |
622 | module_i2c_driver(lm3630a_i2c_driver); |
623 | |
624 | MODULE_DESCRIPTION("Texas Instruments Backlight driver for LM3630A" ); |
625 | MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>" ); |
626 | MODULE_AUTHOR("LDD MLP <ldd-mlp@list.ti.com>" ); |
627 | MODULE_LICENSE("GPL v2" ); |
628 | |