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