backlight: lp855x: move backlight mode platform data
[deliverable/linux.git] / drivers / video / backlight / lp855x_bl.c
CommitLineData
7be865ab
KM
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>
f7f95056 17#include <linux/platform_data/lp855x.h>
8cc9764c 18#include <linux/pwm.h>
7be865ab 19
26e8ccc2
KM
20/* LP8550/1/2/3/6 Registers */
21#define LP855X_BRIGHTNESS_CTRL 0x00
22#define LP855X_DEVICE_CTRL 0x01
23#define LP855X_EEPROM_START 0xA0
24#define LP855X_EEPROM_END 0xA7
25#define LP8556_EPROM_START 0xA0
26#define LP8556_EPROM_END 0xAF
27
28/* LP8557 Registers */
29#define LP8557_BL_CMD 0x00
30#define LP8557_BL_MASK 0x01
31#define LP8557_BL_ON 0x01
32#define LP8557_BL_OFF 0x00
33#define LP8557_BRIGHTNESS_CTRL 0x04
34#define LP8557_CONFIG 0x10
35#define LP8557_EPROM_START 0x10
36#define LP8557_EPROM_END 0x1E
7be865ab 37
7be865ab
KM
38#define DEFAULT_BL_NAME "lcd-backlight"
39#define MAX_BRIGHTNESS 255
40
0b818573
KM
41enum lp855x_brightness_ctrl_mode {
42 PWM_BASED = 1,
43 REGISTER_BASED,
44};
45
68853bc2
KM
46struct lp855x;
47
48/*
49 * struct lp855x_device_config
50 * @pre_init_device: init device function call before updating the brightness
51 * @reg_brightness: register address for brigthenss control
52 * @reg_devicectrl: register address for device control
53 * @post_init_device: late init device function call
54 */
55struct lp855x_device_config {
56 int (*pre_init_device)(struct lp855x *);
57 u8 reg_brightness;
58 u8 reg_devicectrl;
59 int (*post_init_device)(struct lp855x *);
60};
61
7be865ab
KM
62struct lp855x {
63 const char *chipname;
64 enum lp855x_chip_id chip_id;
0b818573 65 enum lp855x_brightness_ctrl_mode mode;
68853bc2 66 struct lp855x_device_config *cfg;
7be865ab
KM
67 struct i2c_client *client;
68 struct backlight_device *bl;
69 struct device *dev;
7be865ab 70 struct lp855x_platform_data *pdata;
8cc9764c 71 struct pwm_device *pwm;
7be865ab
KM
72};
73
7be865ab
KM
74static int lp855x_write_byte(struct lp855x *lp, u8 reg, u8 data)
75{
eaa4d02f 76 return i2c_smbus_write_byte_data(lp->client, reg, data);
7be865ab
KM
77}
78
26e8ccc2
KM
79static int lp855x_update_bit(struct lp855x *lp, u8 reg, u8 mask, u8 data)
80{
81 int ret;
82 u8 tmp;
83
84 ret = i2c_smbus_read_byte_data(lp->client, reg);
85 if (ret < 0) {
86 dev_err(lp->dev, "failed to read 0x%.2x\n", reg);
87 return ret;
88 }
89
90 tmp = (u8)ret;
91 tmp &= ~mask;
92 tmp |= data & mask;
93
94 return lp855x_write_byte(lp, reg, tmp);
95}
96
7be865ab
KM
97static bool lp855x_is_valid_rom_area(struct lp855x *lp, u8 addr)
98{
99 u8 start, end;
100
101 switch (lp->chip_id) {
102 case LP8550:
103 case LP8551:
104 case LP8552:
105 case LP8553:
26e8ccc2
KM
106 start = LP855X_EEPROM_START;
107 end = LP855X_EEPROM_END;
7be865ab
KM
108 break;
109 case LP8556:
26e8ccc2
KM
110 start = LP8556_EPROM_START;
111 end = LP8556_EPROM_END;
112 break;
113 case LP8557:
114 start = LP8557_EPROM_START;
115 end = LP8557_EPROM_END;
7be865ab
KM
116 break;
117 default:
118 return false;
119 }
120
121 return (addr >= start && addr <= end);
122}
123
26e8ccc2
KM
124static int lp8557_bl_off(struct lp855x *lp)
125{
126 /* BL_ON = 0 before updating EPROM settings */
127 return lp855x_update_bit(lp, LP8557_BL_CMD, LP8557_BL_MASK,
128 LP8557_BL_OFF);
129}
130
131static int lp8557_bl_on(struct lp855x *lp)
132{
133 /* BL_ON = 1 after updating EPROM settings */
134 return lp855x_update_bit(lp, LP8557_BL_CMD, LP8557_BL_MASK,
135 LP8557_BL_ON);
136}
137
68853bc2 138static struct lp855x_device_config lp855x_dev_cfg = {
26e8ccc2
KM
139 .reg_brightness = LP855X_BRIGHTNESS_CTRL,
140 .reg_devicectrl = LP855X_DEVICE_CTRL,
141};
142
143static struct lp855x_device_config lp8557_dev_cfg = {
144 .reg_brightness = LP8557_BRIGHTNESS_CTRL,
145 .reg_devicectrl = LP8557_CONFIG,
146 .pre_init_device = lp8557_bl_off,
147 .post_init_device = lp8557_bl_on,
68853bc2
KM
148};
149
150/*
151 * Device specific configuration flow
152 *
153 * a) pre_init_device(optional)
154 * b) update the brightness register
155 * c) update device control register
156 * d) update ROM area(optional)
157 * e) post_init_device(optional)
158 *
159 */
160static int lp855x_configure(struct lp855x *lp)
7be865ab
KM
161{
162 u8 val, addr;
163 int i, ret;
164 struct lp855x_platform_data *pd = lp->pdata;
165
68853bc2
KM
166 switch (lp->chip_id) {
167 case LP8550 ... LP8556:
168 lp->cfg = &lp855x_dev_cfg;
169 break;
26e8ccc2
KM
170 case LP8557:
171 lp->cfg = &lp8557_dev_cfg;
172 break;
68853bc2
KM
173 default:
174 return -EINVAL;
175 }
176
177 if (lp->cfg->pre_init_device) {
178 ret = lp->cfg->pre_init_device(lp);
179 if (ret) {
180 dev_err(lp->dev, "pre init device err: %d\n", ret);
181 goto err;
182 }
183 }
184
7be865ab 185 val = pd->initial_brightness;
68853bc2 186 ret = lp855x_write_byte(lp, lp->cfg->reg_brightness, val);
7be865ab 187 if (ret)
68853bc2 188 goto err;
7be865ab
KM
189
190 val = pd->device_control;
68853bc2 191 ret = lp855x_write_byte(lp, lp->cfg->reg_devicectrl, val);
7be865ab 192 if (ret)
68853bc2 193 goto err;
7be865ab
KM
194
195 if (pd->load_new_rom_data && pd->size_program) {
196 for (i = 0; i < pd->size_program; i++) {
197 addr = pd->rom_data[i].addr;
198 val = pd->rom_data[i].val;
199 if (!lp855x_is_valid_rom_area(lp, addr))
200 continue;
201
202 ret = lp855x_write_byte(lp, addr, val);
203 if (ret)
68853bc2
KM
204 goto err;
205 }
206 }
207
208 if (lp->cfg->post_init_device) {
209 ret = lp->cfg->post_init_device(lp);
210 if (ret) {
211 dev_err(lp->dev, "post init device err: %d\n", ret);
212 goto err;
7be865ab
KM
213 }
214 }
215
68853bc2
KM
216 return 0;
217
218err:
7be865ab
KM
219 return ret;
220}
221
8cc9764c
KM
222static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br)
223{
224 unsigned int period = lp->pdata->period_ns;
225 unsigned int duty = br * period / max_br;
226 struct pwm_device *pwm;
227
228 /* request pwm device with the consumer name */
229 if (!lp->pwm) {
230 pwm = devm_pwm_get(lp->dev, lp->chipname);
231 if (IS_ERR(pwm))
232 return;
233
234 lp->pwm = pwm;
235 }
236
237 pwm_config(lp->pwm, duty, period);
238 if (duty)
239 pwm_enable(lp->pwm);
240 else
241 pwm_disable(lp->pwm);
242}
243
7be865ab
KM
244static int lp855x_bl_update_status(struct backlight_device *bl)
245{
246 struct lp855x *lp = bl_get_data(bl);
7be865ab
KM
247
248 if (bl->props.state & BL_CORE_SUSPENDED)
249 bl->props.brightness = 0;
250
0b818573 251 if (lp->mode == PWM_BASED) {
7be865ab
KM
252 int br = bl->props.brightness;
253 int max_br = bl->props.max_brightness;
254
8cc9764c 255 lp855x_pwm_ctrl(lp, br, max_br);
7be865ab 256
0b818573 257 } else if (lp->mode == REGISTER_BASED) {
7be865ab 258 u8 val = bl->props.brightness;
26e8ccc2 259 lp855x_write_byte(lp, lp->cfg->reg_brightness, val);
7be865ab
KM
260 }
261
262 return 0;
263}
264
265static int lp855x_bl_get_brightness(struct backlight_device *bl)
266{
7be865ab
KM
267 return bl->props.brightness;
268}
269
270static const struct backlight_ops lp855x_bl_ops = {
271 .options = BL_CORE_SUSPENDRESUME,
272 .update_status = lp855x_bl_update_status,
273 .get_brightness = lp855x_bl_get_brightness,
274};
275
276static int lp855x_backlight_register(struct lp855x *lp)
277{
278 struct backlight_device *bl;
279 struct backlight_properties props;
280 struct lp855x_platform_data *pdata = lp->pdata;
600ffd33 281 const char *name = pdata->name ? : DEFAULT_BL_NAME;
7be865ab
KM
282
283 props.type = BACKLIGHT_PLATFORM;
284 props.max_brightness = MAX_BRIGHTNESS;
285
286 if (pdata->initial_brightness > props.max_brightness)
287 pdata->initial_brightness = props.max_brightness;
288
289 props.brightness = pdata->initial_brightness;
290
291 bl = backlight_device_register(name, lp->dev, lp,
292 &lp855x_bl_ops, &props);
293 if (IS_ERR(bl))
294 return PTR_ERR(bl);
295
296 lp->bl = bl;
297
298 return 0;
299}
300
301static void lp855x_backlight_unregister(struct lp855x *lp)
302{
303 if (lp->bl)
304 backlight_device_unregister(lp->bl);
305}
306
307static ssize_t lp855x_get_chip_id(struct device *dev,
308 struct device_attribute *attr, char *buf)
309{
310 struct lp855x *lp = dev_get_drvdata(dev);
6a7aeb1a 311 return scnprintf(buf, PAGE_SIZE, "%s\n", lp->chipname);
7be865ab
KM
312}
313
314static ssize_t lp855x_get_bl_ctl_mode(struct device *dev,
315 struct device_attribute *attr, char *buf)
316{
317 struct lp855x *lp = dev_get_drvdata(dev);
7be865ab
KM
318 char *strmode = NULL;
319
0b818573 320 if (lp->mode == PWM_BASED)
7be865ab 321 strmode = "pwm based";
0b818573 322 else if (lp->mode == REGISTER_BASED)
7be865ab
KM
323 strmode = "register based";
324
6a7aeb1a 325 return scnprintf(buf, PAGE_SIZE, "%s\n", strmode);
7be865ab
KM
326}
327
328static DEVICE_ATTR(chip_id, S_IRUGO, lp855x_get_chip_id, NULL);
329static DEVICE_ATTR(bl_ctl_mode, S_IRUGO, lp855x_get_bl_ctl_mode, NULL);
330
331static struct attribute *lp855x_attributes[] = {
332 &dev_attr_chip_id.attr,
333 &dev_attr_bl_ctl_mode.attr,
334 NULL,
335};
336
337static const struct attribute_group lp855x_attr_group = {
338 .attrs = lp855x_attributes,
339};
340
341static int lp855x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
342{
343 struct lp855x *lp;
344 struct lp855x_platform_data *pdata = cl->dev.platform_data;
7be865ab
KM
345 int ret;
346
347 if (!pdata) {
348 dev_err(&cl->dev, "no platform data supplied\n");
349 return -EINVAL;
350 }
351
352 if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
353 return -EIO;
354
355 lp = devm_kzalloc(&cl->dev, sizeof(struct lp855x), GFP_KERNEL);
356 if (!lp)
357 return -ENOMEM;
358
0b818573
KM
359 if (pdata->period_ns > 0)
360 lp->mode = PWM_BASED;
361 else
362 lp->mode = REGISTER_BASED;
363
7be865ab
KM
364 lp->client = cl;
365 lp->dev = &cl->dev;
366 lp->pdata = pdata;
367 lp->chipname = id->name;
368 lp->chip_id = id->driver_data;
369 i2c_set_clientdata(cl, lp);
370
68853bc2 371 ret = lp855x_configure(lp);
7be865ab 372 if (ret) {
68853bc2
KM
373 dev_err(lp->dev, "device config err: %d", ret);
374 goto err_dev;
7be865ab
KM
375 }
376
377 ret = lp855x_backlight_register(lp);
378 if (ret) {
379 dev_err(lp->dev,
380 "failed to register backlight. err: %d\n", ret);
381 goto err_dev;
382 }
383
384 ret = sysfs_create_group(&lp->dev->kobj, &lp855x_attr_group);
385 if (ret) {
386 dev_err(lp->dev, "failed to register sysfs. err: %d\n", ret);
387 goto err_sysfs;
388 }
389
390 backlight_update_status(lp->bl);
391 return 0;
392
393err_sysfs:
394 lp855x_backlight_unregister(lp);
395err_dev:
396 return ret;
397}
398
7e4b9d0b 399static int lp855x_remove(struct i2c_client *cl)
7be865ab
KM
400{
401 struct lp855x *lp = i2c_get_clientdata(cl);
402
403 lp->bl->props.brightness = 0;
404 backlight_update_status(lp->bl);
405 sysfs_remove_group(&lp->dev->kobj, &lp855x_attr_group);
406 lp855x_backlight_unregister(lp);
407
408 return 0;
409}
410
411static const struct i2c_device_id lp855x_ids[] = {
412 {"lp8550", LP8550},
413 {"lp8551", LP8551},
414 {"lp8552", LP8552},
415 {"lp8553", LP8553},
416 {"lp8556", LP8556},
26e8ccc2 417 {"lp8557", LP8557},
7be865ab
KM
418 { }
419};
420MODULE_DEVICE_TABLE(i2c, lp855x_ids);
421
422static struct i2c_driver lp855x_driver = {
423 .driver = {
424 .name = "lp855x",
425 },
426 .probe = lp855x_probe,
d1723fa2 427 .remove = lp855x_remove,
7be865ab
KM
428 .id_table = lp855x_ids,
429};
430
431module_i2c_driver(lp855x_driver);
432
433MODULE_DESCRIPTION("Texas Instruments LP855x Backlight driver");
434MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");
435MODULE_LICENSE("GPL");
This page took 0.129666 seconds and 5 git commands to generate.