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