• Home
  • History
  • Annotate
  • only in this directory
1/*
2 * TI LP855x Backlight Driver
3 *
4 *			Copyright (C) 2011 Texas Instruments
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 */
11
12#include <linux/module.h>
13#include <linux/slab.h>
14#include <linux/i2c.h>
15#include <linux/backlight.h>
16#include <linux/err.h>
17#include <linux/of.h>
18#include <linux/platform_data/lp855x.h>
19#include <linux/pwm.h>
20#include <linux/regulator/consumer.h>
21
22/* LP8550/1/2/3/6 Registers */
23#define LP855X_BRIGHTNESS_CTRL		0x00
24#define LP855X_DEVICE_CTRL		0x01
25#define LP855X_EEPROM_START		0xA0
26#define LP855X_EEPROM_END		0xA7
27#define LP8556_EPROM_START		0xA0
28#define LP8556_EPROM_END		0xAF
29
30/* LP8555/7 Registers */
31#define LP8557_BL_CMD			0x00
32#define LP8557_BL_MASK			0x01
33#define LP8557_BL_ON			0x01
34#define LP8557_BL_OFF			0x00
35#define LP8557_BRIGHTNESS_CTRL		0x04
36#define LP8557_CONFIG			0x10
37#define LP8555_EPROM_START		0x10
38#define LP8555_EPROM_END		0x7A
39#define LP8557_EPROM_START		0x10
40#define LP8557_EPROM_END		0x1E
41
42#define DEFAULT_BL_NAME		"lcd-backlight"
43#define MAX_BRIGHTNESS		255
44
45enum lp855x_brightness_ctrl_mode {
46	PWM_BASED = 1,
47	REGISTER_BASED,
48};
49
50struct lp855x;
51
52/*
53 * struct lp855x_device_config
54 * @pre_init_device: init device function call before updating the brightness
55 * @reg_brightness: register address for brigthenss control
56 * @reg_devicectrl: register address for device control
57 * @post_init_device: late init device function call
58 */
59struct lp855x_device_config {
60	int (*pre_init_device)(struct lp855x *);
61	u8 reg_brightness;
62	u8 reg_devicectrl;
63	int (*post_init_device)(struct lp855x *);
64};
65
66struct lp855x {
67	const char *chipname;
68	enum lp855x_chip_id chip_id;
69	enum lp855x_brightness_ctrl_mode mode;
70	struct lp855x_device_config *cfg;
71	struct i2c_client *client;
72	struct backlight_device *bl;
73	struct device *dev;
74	struct lp855x_platform_data *pdata;
75	struct pwm_device *pwm;
76};
77
78static int lp855x_write_byte(struct lp855x *lp, u8 reg, u8 data)
79{
80	return i2c_smbus_write_byte_data(lp->client, reg, data);
81}
82
83static int lp855x_update_bit(struct lp855x *lp, u8 reg, u8 mask, u8 data)
84{
85	int ret;
86	u8 tmp;
87
88	ret = i2c_smbus_read_byte_data(lp->client, reg);
89	if (ret < 0) {
90		dev_err(lp->dev, "failed to read 0x%.2x\n", reg);
91		return ret;
92	}
93
94	tmp = (u8)ret;
95	tmp &= ~mask;
96	tmp |= data & mask;
97
98	return lp855x_write_byte(lp, reg, tmp);
99}
100
101static bool lp855x_is_valid_rom_area(struct lp855x *lp, u8 addr)
102{
103	u8 start, end;
104
105	switch (lp->chip_id) {
106	case LP8550:
107	case LP8551:
108	case LP8552:
109	case LP8553:
110		start = LP855X_EEPROM_START;
111		end = LP855X_EEPROM_END;
112		break;
113	case LP8556:
114		start = LP8556_EPROM_START;
115		end = LP8556_EPROM_END;
116		break;
117	case LP8555:
118		start = LP8555_EPROM_START;
119		end = LP8555_EPROM_END;
120		break;
121	case LP8557:
122		start = LP8557_EPROM_START;
123		end = LP8557_EPROM_END;
124		break;
125	default:
126		return false;
127	}
128
129	return addr >= start && addr <= end;
130}
131
132static int lp8557_bl_off(struct lp855x *lp)
133{
134	/* BL_ON = 0 before updating EPROM settings */
135	return lp855x_update_bit(lp, LP8557_BL_CMD, LP8557_BL_MASK,
136				LP8557_BL_OFF);
137}
138
139static int lp8557_bl_on(struct lp855x *lp)
140{
141	/* BL_ON = 1 after updating EPROM settings */
142	return lp855x_update_bit(lp, LP8557_BL_CMD, LP8557_BL_MASK,
143				LP8557_BL_ON);
144}
145
146static struct lp855x_device_config lp855x_dev_cfg = {
147	.reg_brightness = LP855X_BRIGHTNESS_CTRL,
148	.reg_devicectrl = LP855X_DEVICE_CTRL,
149};
150
151static struct lp855x_device_config lp8557_dev_cfg = {
152	.reg_brightness = LP8557_BRIGHTNESS_CTRL,
153	.reg_devicectrl = LP8557_CONFIG,
154	.pre_init_device = lp8557_bl_off,
155	.post_init_device = lp8557_bl_on,
156};
157
158/*
159 * Device specific configuration flow
160 *
161 *    a) pre_init_device(optional)
162 *    b) update the brightness register
163 *    c) update device control register
164 *    d) update ROM area(optional)
165 *    e) post_init_device(optional)
166 *
167 */
168static int lp855x_configure(struct lp855x *lp)
169{
170	u8 val, addr;
171	int i, ret;
172	struct lp855x_platform_data *pd = lp->pdata;
173
174	switch (lp->chip_id) {
175	case LP8550:
176	case LP8551:
177	case LP8552:
178	case LP8553:
179	case LP8556:
180		lp->cfg = &lp855x_dev_cfg;
181		break;
182	case LP8555:
183	case LP8557:
184		lp->cfg = &lp8557_dev_cfg;
185		break;
186	default:
187		return -EINVAL;
188	}
189
190	if (lp->cfg->pre_init_device) {
191		ret = lp->cfg->pre_init_device(lp);
192		if (ret) {
193			dev_err(lp->dev, "pre init device err: %d\n", ret);
194			goto err;
195		}
196	}
197
198	val = pd->initial_brightness;
199	ret = lp855x_write_byte(lp, lp->cfg->reg_brightness, val);
200	if (ret)
201		goto err;
202
203	val = pd->device_control;
204	ret = lp855x_write_byte(lp, lp->cfg->reg_devicectrl, val);
205	if (ret)
206		goto err;
207
208	if (pd->size_program > 0) {
209		for (i = 0; i < pd->size_program; i++) {
210			addr = pd->rom_data[i].addr;
211			val = pd->rom_data[i].val;
212			if (!lp855x_is_valid_rom_area(lp, addr))
213				continue;
214
215			ret = lp855x_write_byte(lp, addr, val);
216			if (ret)
217				goto err;
218		}
219	}
220
221	if (lp->cfg->post_init_device) {
222		ret = lp->cfg->post_init_device(lp);
223		if (ret) {
224			dev_err(lp->dev, "post init device err: %d\n", ret);
225			goto err;
226		}
227	}
228
229	return 0;
230
231err:
232	return ret;
233}
234
235static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br)
236{
237	unsigned int period = lp->pdata->period_ns;
238	unsigned int duty = br * period / max_br;
239	struct pwm_device *pwm;
240
241	/* request pwm device with the consumer name */
242	if (!lp->pwm) {
243		pwm = devm_pwm_get(lp->dev, lp->chipname);
244		if (IS_ERR(pwm))
245			return;
246
247		lp->pwm = pwm;
248	}
249
250	pwm_config(lp->pwm, duty, period);
251	if (duty)
252		pwm_enable(lp->pwm);
253	else
254		pwm_disable(lp->pwm);
255}
256
257static int lp855x_bl_update_status(struct backlight_device *bl)
258{
259	struct lp855x *lp = bl_get_data(bl);
260
261	if (bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
262		bl->props.brightness = 0;
263
264	if (lp->mode == PWM_BASED) {
265		int br = bl->props.brightness;
266		int max_br = bl->props.max_brightness;
267
268		lp855x_pwm_ctrl(lp, br, max_br);
269
270	} else if (lp->mode == REGISTER_BASED) {
271		u8 val = bl->props.brightness;
272
273		lp855x_write_byte(lp, lp->cfg->reg_brightness, val);
274	}
275
276	return 0;
277}
278
279static const struct backlight_ops lp855x_bl_ops = {
280	.options = BL_CORE_SUSPENDRESUME,
281	.update_status = lp855x_bl_update_status,
282};
283
284static int lp855x_backlight_register(struct lp855x *lp)
285{
286	struct backlight_device *bl;
287	struct backlight_properties props;
288	struct lp855x_platform_data *pdata = lp->pdata;
289	const char *name = pdata->name ? : DEFAULT_BL_NAME;
290
291	props.type = BACKLIGHT_PLATFORM;
292	props.max_brightness = MAX_BRIGHTNESS;
293
294	if (pdata->initial_brightness > props.max_brightness)
295		pdata->initial_brightness = props.max_brightness;
296
297	props.brightness = pdata->initial_brightness;
298
299	bl = devm_backlight_device_register(lp->dev, name, lp->dev, lp,
300				       &lp855x_bl_ops, &props);
301	if (IS_ERR(bl))
302		return PTR_ERR(bl);
303
304	lp->bl = bl;
305
306	return 0;
307}
308
309static ssize_t lp855x_get_chip_id(struct device *dev,
310				struct device_attribute *attr, char *buf)
311{
312	struct lp855x *lp = dev_get_drvdata(dev);
313
314	return scnprintf(buf, PAGE_SIZE, "%s\n", lp->chipname);
315}
316
317static ssize_t lp855x_get_bl_ctl_mode(struct device *dev,
318				     struct device_attribute *attr, char *buf)
319{
320	struct lp855x *lp = dev_get_drvdata(dev);
321	char *strmode = NULL;
322
323	if (lp->mode == PWM_BASED)
324		strmode = "pwm based";
325	else if (lp->mode == REGISTER_BASED)
326		strmode = "register based";
327
328	return scnprintf(buf, PAGE_SIZE, "%s\n", strmode);
329}
330
331static DEVICE_ATTR(chip_id, S_IRUGO, lp855x_get_chip_id, NULL);
332static DEVICE_ATTR(bl_ctl_mode, S_IRUGO, lp855x_get_bl_ctl_mode, NULL);
333
334static struct attribute *lp855x_attributes[] = {
335	&dev_attr_chip_id.attr,
336	&dev_attr_bl_ctl_mode.attr,
337	NULL,
338};
339
340static const struct attribute_group lp855x_attr_group = {
341	.attrs = lp855x_attributes,
342};
343
344#ifdef CONFIG_OF
345static int lp855x_parse_dt(struct lp855x *lp)
346{
347	struct device *dev = lp->dev;
348	struct device_node *node = dev->of_node;
349	struct lp855x_platform_data *pdata;
350	int rom_length;
351
352	if (!node) {
353		dev_err(dev, "no platform data\n");
354		return -EINVAL;
355	}
356
357	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
358	if (!pdata)
359		return -ENOMEM;
360
361	of_property_read_string(node, "bl-name", &pdata->name);
362	of_property_read_u8(node, "dev-ctrl", &pdata->device_control);
363	of_property_read_u8(node, "init-brt", &pdata->initial_brightness);
364	of_property_read_u32(node, "pwm-period", &pdata->period_ns);
365
366	/* Fill ROM platform data if defined */
367	rom_length = of_get_child_count(node);
368	if (rom_length > 0) {
369		struct lp855x_rom_data *rom;
370		struct device_node *child;
371		int i = 0;
372
373		rom = devm_kzalloc(dev, sizeof(*rom) * rom_length, GFP_KERNEL);
374		if (!rom)
375			return -ENOMEM;
376
377		for_each_child_of_node(node, child) {
378			of_property_read_u8(child, "rom-addr", &rom[i].addr);
379			of_property_read_u8(child, "rom-val", &rom[i].val);
380			i++;
381		}
382
383		pdata->size_program = rom_length;
384		pdata->rom_data = &rom[0];
385	}
386
387	pdata->supply = devm_regulator_get(dev, "power");
388	if (IS_ERR(pdata->supply)) {
389		if (PTR_ERR(pdata->supply) == -EPROBE_DEFER)
390			return -EPROBE_DEFER;
391		pdata->supply = NULL;
392	}
393
394	lp->pdata = pdata;
395
396	return 0;
397}
398#else
399static int lp855x_parse_dt(struct lp855x *lp)
400{
401	return -EINVAL;
402}
403#endif
404
405static int lp855x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
406{
407	struct lp855x *lp;
408	int ret;
409
410	if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
411		return -EIO;
412
413	lp = devm_kzalloc(&cl->dev, sizeof(struct lp855x), GFP_KERNEL);
414	if (!lp)
415		return -ENOMEM;
416
417	lp->client = cl;
418	lp->dev = &cl->dev;
419	lp->chipname = id->name;
420	lp->chip_id = id->driver_data;
421	lp->pdata = dev_get_platdata(&cl->dev);
422
423	if (!lp->pdata) {
424		ret = lp855x_parse_dt(lp);
425		if (ret < 0)
426			return ret;
427	}
428
429	if (lp->pdata->period_ns > 0)
430		lp->mode = PWM_BASED;
431	else
432		lp->mode = REGISTER_BASED;
433
434	if (lp->pdata->supply) {
435		ret = regulator_enable(lp->pdata->supply);
436		if (ret < 0) {
437			dev_err(&cl->dev, "failed to enable supply: %d\n", ret);
438			return ret;
439		}
440	}
441
442	i2c_set_clientdata(cl, lp);
443
444	ret = lp855x_configure(lp);
445	if (ret) {
446		dev_err(lp->dev, "device config err: %d", ret);
447		return ret;
448	}
449
450	ret = lp855x_backlight_register(lp);
451	if (ret) {
452		dev_err(lp->dev,
453			"failed to register backlight. err: %d\n", ret);
454		return ret;
455	}
456
457	ret = sysfs_create_group(&lp->dev->kobj, &lp855x_attr_group);
458	if (ret) {
459		dev_err(lp->dev, "failed to register sysfs. err: %d\n", ret);
460		return ret;
461	}
462
463	backlight_update_status(lp->bl);
464	return 0;
465}
466
467static int lp855x_remove(struct i2c_client *cl)
468{
469	struct lp855x *lp = i2c_get_clientdata(cl);
470
471	lp->bl->props.brightness = 0;
472	backlight_update_status(lp->bl);
473	if (lp->pdata->supply)
474		regulator_disable(lp->pdata->supply);
475	sysfs_remove_group(&lp->dev->kobj, &lp855x_attr_group);
476
477	return 0;
478}
479
480static const struct of_device_id lp855x_dt_ids[] = {
481	{ .compatible = "ti,lp8550", },
482	{ .compatible = "ti,lp8551", },
483	{ .compatible = "ti,lp8552", },
484	{ .compatible = "ti,lp8553", },
485	{ .compatible = "ti,lp8555", },
486	{ .compatible = "ti,lp8556", },
487	{ .compatible = "ti,lp8557", },
488	{ }
489};
490MODULE_DEVICE_TABLE(of, lp855x_dt_ids);
491
492static const struct i2c_device_id lp855x_ids[] = {
493	{"lp8550", LP8550},
494	{"lp8551", LP8551},
495	{"lp8552", LP8552},
496	{"lp8553", LP8553},
497	{"lp8555", LP8555},
498	{"lp8556", LP8556},
499	{"lp8557", LP8557},
500	{ }
501};
502MODULE_DEVICE_TABLE(i2c, lp855x_ids);
503
504static struct i2c_driver lp855x_driver = {
505	.driver = {
506		   .name = "lp855x",
507		   .of_match_table = of_match_ptr(lp855x_dt_ids),
508		   },
509	.probe = lp855x_probe,
510	.remove = lp855x_remove,
511	.id_table = lp855x_ids,
512};
513
514module_i2c_driver(lp855x_driver);
515
516MODULE_DESCRIPTION("Texas Instruments LP855x Backlight driver");
517MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");
518MODULE_LICENSE("GPL");
519