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/platform_data/lp855x.h>
18 #include <linux/pwm.h>
19 
20 /* Registers */
21 #define BRIGHTNESS_CTRL		0x00
22 #define DEVICE_CTRL		0x01
23 #define EEPROM_START		0xA0
24 #define EEPROM_END		0xA7
25 #define EPROM_START		0xA0
26 #define EPROM_END		0xAF
27 
28 #define BUF_SIZE		20
29 #define DEFAULT_BL_NAME		"lcd-backlight"
30 #define MAX_BRIGHTNESS		255
31 
32 struct lp855x {
33 	const char *chipname;
34 	enum lp855x_chip_id chip_id;
35 	struct i2c_client *client;
36 	struct backlight_device *bl;
37 	struct device *dev;
38 	struct lp855x_platform_data *pdata;
39 	struct pwm_device *pwm;
40 };
41 
42 static int lp855x_read_byte(struct lp855x *lp, u8 reg, u8 *data)
43 {
44 	int ret;
45 
46 	ret = i2c_smbus_read_byte_data(lp->client, reg);
47 	if (ret < 0) {
48 		dev_err(lp->dev, "failed to read 0x%.2x\n", reg);
49 		return ret;
50 	}
51 
52 	*data = (u8)ret;
53 	return 0;
54 }
55 
56 static int lp855x_write_byte(struct lp855x *lp, u8 reg, u8 data)
57 {
58 	return i2c_smbus_write_byte_data(lp->client, reg, data);
59 }
60 
61 static bool lp855x_is_valid_rom_area(struct lp855x *lp, u8 addr)
62 {
63 	u8 start, end;
64 
65 	switch (lp->chip_id) {
66 	case LP8550:
67 	case LP8551:
68 	case LP8552:
69 	case LP8553:
70 		start = EEPROM_START;
71 		end = EEPROM_END;
72 		break;
73 	case LP8556:
74 		start = EPROM_START;
75 		end = EPROM_END;
76 		break;
77 	default:
78 		return false;
79 	}
80 
81 	return (addr >= start && addr <= end);
82 }
83 
84 static int lp855x_init_registers(struct lp855x *lp)
85 {
86 	u8 val, addr;
87 	int i, ret;
88 	struct lp855x_platform_data *pd = lp->pdata;
89 
90 	val = pd->initial_brightness;
91 	ret = lp855x_write_byte(lp, BRIGHTNESS_CTRL, val);
92 	if (ret)
93 		return ret;
94 
95 	val = pd->device_control;
96 	ret = lp855x_write_byte(lp, DEVICE_CTRL, val);
97 	if (ret)
98 		return ret;
99 
100 	if (pd->load_new_rom_data && pd->size_program) {
101 		for (i = 0; i < pd->size_program; i++) {
102 			addr = pd->rom_data[i].addr;
103 			val = pd->rom_data[i].val;
104 			if (!lp855x_is_valid_rom_area(lp, addr))
105 				continue;
106 
107 			ret = lp855x_write_byte(lp, addr, val);
108 			if (ret)
109 				return ret;
110 		}
111 	}
112 
113 	return ret;
114 }
115 
116 static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br)
117 {
118 	unsigned int period = lp->pdata->period_ns;
119 	unsigned int duty = br * period / max_br;
120 	struct pwm_device *pwm;
121 
122 	/* request pwm device with the consumer name */
123 	if (!lp->pwm) {
124 		pwm = devm_pwm_get(lp->dev, lp->chipname);
125 		if (IS_ERR(pwm))
126 			return;
127 
128 		lp->pwm = pwm;
129 	}
130 
131 	pwm_config(lp->pwm, duty, period);
132 	if (duty)
133 		pwm_enable(lp->pwm);
134 	else
135 		pwm_disable(lp->pwm);
136 }
137 
138 static int lp855x_bl_update_status(struct backlight_device *bl)
139 {
140 	struct lp855x *lp = bl_get_data(bl);
141 	enum lp855x_brightness_ctrl_mode mode = lp->pdata->mode;
142 
143 	if (bl->props.state & BL_CORE_SUSPENDED)
144 		bl->props.brightness = 0;
145 
146 	if (mode == PWM_BASED) {
147 		int br = bl->props.brightness;
148 		int max_br = bl->props.max_brightness;
149 
150 		lp855x_pwm_ctrl(lp, br, max_br);
151 
152 	} else if (mode == REGISTER_BASED) {
153 		u8 val = bl->props.brightness;
154 		lp855x_write_byte(lp, BRIGHTNESS_CTRL, val);
155 	}
156 
157 	return 0;
158 }
159 
160 static int lp855x_bl_get_brightness(struct backlight_device *bl)
161 {
162 	struct lp855x *lp = bl_get_data(bl);
163 	enum lp855x_brightness_ctrl_mode mode = lp->pdata->mode;
164 
165 	if (mode == REGISTER_BASED) {
166 		u8 val = 0;
167 
168 		lp855x_read_byte(lp, BRIGHTNESS_CTRL, &val);
169 		bl->props.brightness = val;
170 	}
171 
172 	return bl->props.brightness;
173 }
174 
175 static const struct backlight_ops lp855x_bl_ops = {
176 	.options = BL_CORE_SUSPENDRESUME,
177 	.update_status = lp855x_bl_update_status,
178 	.get_brightness = lp855x_bl_get_brightness,
179 };
180 
181 static int lp855x_backlight_register(struct lp855x *lp)
182 {
183 	struct backlight_device *bl;
184 	struct backlight_properties props;
185 	struct lp855x_platform_data *pdata = lp->pdata;
186 	char *name = pdata->name ? : DEFAULT_BL_NAME;
187 
188 	props.type = BACKLIGHT_PLATFORM;
189 	props.max_brightness = MAX_BRIGHTNESS;
190 
191 	if (pdata->initial_brightness > props.max_brightness)
192 		pdata->initial_brightness = props.max_brightness;
193 
194 	props.brightness = pdata->initial_brightness;
195 
196 	bl = backlight_device_register(name, lp->dev, lp,
197 				       &lp855x_bl_ops, &props);
198 	if (IS_ERR(bl))
199 		return PTR_ERR(bl);
200 
201 	lp->bl = bl;
202 
203 	return 0;
204 }
205 
206 static void lp855x_backlight_unregister(struct lp855x *lp)
207 {
208 	if (lp->bl)
209 		backlight_device_unregister(lp->bl);
210 }
211 
212 static ssize_t lp855x_get_chip_id(struct device *dev,
213 				struct device_attribute *attr, char *buf)
214 {
215 	struct lp855x *lp = dev_get_drvdata(dev);
216 	return scnprintf(buf, BUF_SIZE, "%s\n", lp->chipname);
217 }
218 
219 static ssize_t lp855x_get_bl_ctl_mode(struct device *dev,
220 				     struct device_attribute *attr, char *buf)
221 {
222 	struct lp855x *lp = dev_get_drvdata(dev);
223 	enum lp855x_brightness_ctrl_mode mode = lp->pdata->mode;
224 	char *strmode = NULL;
225 
226 	if (mode == PWM_BASED)
227 		strmode = "pwm based";
228 	else if (mode == REGISTER_BASED)
229 		strmode = "register based";
230 
231 	return scnprintf(buf, BUF_SIZE, "%s\n", strmode);
232 }
233 
234 static DEVICE_ATTR(chip_id, S_IRUGO, lp855x_get_chip_id, NULL);
235 static DEVICE_ATTR(bl_ctl_mode, S_IRUGO, lp855x_get_bl_ctl_mode, NULL);
236 
237 static struct attribute *lp855x_attributes[] = {
238 	&dev_attr_chip_id.attr,
239 	&dev_attr_bl_ctl_mode.attr,
240 	NULL,
241 };
242 
243 static const struct attribute_group lp855x_attr_group = {
244 	.attrs = lp855x_attributes,
245 };
246 
247 static int lp855x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
248 {
249 	struct lp855x *lp;
250 	struct lp855x_platform_data *pdata = cl->dev.platform_data;
251 	enum lp855x_brightness_ctrl_mode mode;
252 	int ret;
253 
254 	if (!pdata) {
255 		dev_err(&cl->dev, "no platform data supplied\n");
256 		return -EINVAL;
257 	}
258 
259 	if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
260 		return -EIO;
261 
262 	lp = devm_kzalloc(&cl->dev, sizeof(struct lp855x), GFP_KERNEL);
263 	if (!lp)
264 		return -ENOMEM;
265 
266 	mode = pdata->mode;
267 	lp->client = cl;
268 	lp->dev = &cl->dev;
269 	lp->pdata = pdata;
270 	lp->chipname = id->name;
271 	lp->chip_id = id->driver_data;
272 	i2c_set_clientdata(cl, lp);
273 
274 	ret = lp855x_init_registers(lp);
275 	if (ret) {
276 		dev_err(lp->dev, "i2c communication err: %d", ret);
277 		if (mode == REGISTER_BASED)
278 			goto err_dev;
279 	}
280 
281 	ret = lp855x_backlight_register(lp);
282 	if (ret) {
283 		dev_err(lp->dev,
284 			"failed to register backlight. err: %d\n", ret);
285 		goto err_dev;
286 	}
287 
288 	ret = sysfs_create_group(&lp->dev->kobj, &lp855x_attr_group);
289 	if (ret) {
290 		dev_err(lp->dev, "failed to register sysfs. err: %d\n", ret);
291 		goto err_sysfs;
292 	}
293 
294 	backlight_update_status(lp->bl);
295 	return 0;
296 
297 err_sysfs:
298 	lp855x_backlight_unregister(lp);
299 err_dev:
300 	return ret;
301 }
302 
303 static int lp855x_remove(struct i2c_client *cl)
304 {
305 	struct lp855x *lp = i2c_get_clientdata(cl);
306 
307 	lp->bl->props.brightness = 0;
308 	backlight_update_status(lp->bl);
309 	sysfs_remove_group(&lp->dev->kobj, &lp855x_attr_group);
310 	lp855x_backlight_unregister(lp);
311 
312 	return 0;
313 }
314 
315 static const struct i2c_device_id lp855x_ids[] = {
316 	{"lp8550", LP8550},
317 	{"lp8551", LP8551},
318 	{"lp8552", LP8552},
319 	{"lp8553", LP8553},
320 	{"lp8556", LP8556},
321 	{ }
322 };
323 MODULE_DEVICE_TABLE(i2c, lp855x_ids);
324 
325 static struct i2c_driver lp855x_driver = {
326 	.driver = {
327 		   .name = "lp855x",
328 		   },
329 	.probe = lp855x_probe,
330 	.remove = lp855x_remove,
331 	.id_table = lp855x_ids,
332 };
333 
334 module_i2c_driver(lp855x_driver);
335 
336 MODULE_DESCRIPTION("Texas Instruments LP855x Backlight driver");
337 MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");
338 MODULE_LICENSE("GPL");
339