1 /* 2 * lm3533-bl.c -- LM3533 Backlight driver 3 * 4 * Copyright (C) 2011-2012 Texas Instruments 5 * 6 * Author: Johan Hovold <jhovold@gmail.com> 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU General Public License as published by the 10 * Free Software Foundation; either version 2 of the License, or (at your 11 * option) any later version. 12 */ 13 14 #include <linux/module.h> 15 #include <linux/init.h> 16 #include <linux/platform_device.h> 17 #include <linux/backlight.h> 18 #include <linux/fb.h> 19 #include <linux/slab.h> 20 21 #include <linux/mfd/lm3533.h> 22 23 24 #define LM3533_HVCTRLBANK_COUNT 2 25 #define LM3533_BL_MAX_BRIGHTNESS 255 26 27 #define LM3533_REG_CTRLBANK_AB_BCONF 0x1a 28 29 30 struct lm3533_bl { 31 struct lm3533 *lm3533; 32 struct lm3533_ctrlbank cb; 33 struct backlight_device *bd; 34 int id; 35 }; 36 37 38 static inline int lm3533_bl_get_ctrlbank_id(struct lm3533_bl *bl) 39 { 40 return bl->id; 41 } 42 43 static int lm3533_bl_update_status(struct backlight_device *bd) 44 { 45 struct lm3533_bl *bl = bl_get_data(bd); 46 int brightness = bd->props.brightness; 47 48 if (bd->props.power != FB_BLANK_UNBLANK) 49 brightness = 0; 50 if (bd->props.fb_blank != FB_BLANK_UNBLANK) 51 brightness = 0; 52 53 return lm3533_ctrlbank_set_brightness(&bl->cb, (u8)brightness); 54 } 55 56 static int lm3533_bl_get_brightness(struct backlight_device *bd) 57 { 58 struct lm3533_bl *bl = bl_get_data(bd); 59 u8 val; 60 int ret; 61 62 ret = lm3533_ctrlbank_get_brightness(&bl->cb, &val); 63 if (ret) 64 return ret; 65 66 return val; 67 } 68 69 static const struct backlight_ops lm3533_bl_ops = { 70 .get_brightness = lm3533_bl_get_brightness, 71 .update_status = lm3533_bl_update_status, 72 }; 73 74 static ssize_t show_id(struct device *dev, 75 struct device_attribute *attr, char *buf) 76 { 77 struct lm3533_bl *bl = dev_get_drvdata(dev); 78 79 return scnprintf(buf, PAGE_SIZE, "%d\n", bl->id); 80 } 81 82 static ssize_t show_als_channel(struct device *dev, 83 struct device_attribute *attr, char *buf) 84 { 85 struct lm3533_bl *bl = dev_get_drvdata(dev); 86 unsigned channel = lm3533_bl_get_ctrlbank_id(bl); 87 88 return scnprintf(buf, PAGE_SIZE, "%u\n", channel); 89 } 90 91 static ssize_t show_als_en(struct device *dev, 92 struct device_attribute *attr, char *buf) 93 { 94 struct lm3533_bl *bl = dev_get_drvdata(dev); 95 int ctrlbank = lm3533_bl_get_ctrlbank_id(bl); 96 u8 val; 97 u8 mask; 98 bool enable; 99 int ret; 100 101 ret = lm3533_read(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, &val); 102 if (ret) 103 return ret; 104 105 mask = 1 << (2 * ctrlbank); 106 enable = val & mask; 107 108 return scnprintf(buf, PAGE_SIZE, "%d\n", enable); 109 } 110 111 static ssize_t store_als_en(struct device *dev, 112 struct device_attribute *attr, 113 const char *buf, size_t len) 114 { 115 struct lm3533_bl *bl = dev_get_drvdata(dev); 116 int ctrlbank = lm3533_bl_get_ctrlbank_id(bl); 117 int enable; 118 u8 val; 119 u8 mask; 120 int ret; 121 122 if (kstrtoint(buf, 0, &enable)) 123 return -EINVAL; 124 125 mask = 1 << (2 * ctrlbank); 126 127 if (enable) 128 val = mask; 129 else 130 val = 0; 131 132 ret = lm3533_update(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, val, 133 mask); 134 if (ret) 135 return ret; 136 137 return len; 138 } 139 140 static ssize_t show_linear(struct device *dev, 141 struct device_attribute *attr, char *buf) 142 { 143 struct lm3533_bl *bl = dev_get_drvdata(dev); 144 u8 val; 145 u8 mask; 146 int linear; 147 int ret; 148 149 ret = lm3533_read(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, &val); 150 if (ret) 151 return ret; 152 153 mask = 1 << (2 * lm3533_bl_get_ctrlbank_id(bl) + 1); 154 155 if (val & mask) 156 linear = 1; 157 else 158 linear = 0; 159 160 return scnprintf(buf, PAGE_SIZE, "%x\n", linear); 161 } 162 163 static ssize_t store_linear(struct device *dev, 164 struct device_attribute *attr, 165 const char *buf, size_t len) 166 { 167 struct lm3533_bl *bl = dev_get_drvdata(dev); 168 unsigned long linear; 169 u8 mask; 170 u8 val; 171 int ret; 172 173 if (kstrtoul(buf, 0, &linear)) 174 return -EINVAL; 175 176 mask = 1 << (2 * lm3533_bl_get_ctrlbank_id(bl) + 1); 177 178 if (linear) 179 val = mask; 180 else 181 val = 0; 182 183 ret = lm3533_update(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, val, 184 mask); 185 if (ret) 186 return ret; 187 188 return len; 189 } 190 191 static ssize_t show_pwm(struct device *dev, 192 struct device_attribute *attr, 193 char *buf) 194 { 195 struct lm3533_bl *bl = dev_get_drvdata(dev); 196 u8 val; 197 int ret; 198 199 ret = lm3533_ctrlbank_get_pwm(&bl->cb, &val); 200 if (ret) 201 return ret; 202 203 return scnprintf(buf, PAGE_SIZE, "%u\n", val); 204 } 205 206 static ssize_t store_pwm(struct device *dev, 207 struct device_attribute *attr, 208 const char *buf, size_t len) 209 { 210 struct lm3533_bl *bl = dev_get_drvdata(dev); 211 u8 val; 212 int ret; 213 214 if (kstrtou8(buf, 0, &val)) 215 return -EINVAL; 216 217 ret = lm3533_ctrlbank_set_pwm(&bl->cb, val); 218 if (ret) 219 return ret; 220 221 return len; 222 } 223 224 static LM3533_ATTR_RO(als_channel); 225 static LM3533_ATTR_RW(als_en); 226 static LM3533_ATTR_RO(id); 227 static LM3533_ATTR_RW(linear); 228 static LM3533_ATTR_RW(pwm); 229 230 static struct attribute *lm3533_bl_attributes[] = { 231 &dev_attr_als_channel.attr, 232 &dev_attr_als_en.attr, 233 &dev_attr_id.attr, 234 &dev_attr_linear.attr, 235 &dev_attr_pwm.attr, 236 NULL, 237 }; 238 239 static umode_t lm3533_bl_attr_is_visible(struct kobject *kobj, 240 struct attribute *attr, int n) 241 { 242 struct device *dev = container_of(kobj, struct device, kobj); 243 struct lm3533_bl *bl = dev_get_drvdata(dev); 244 umode_t mode = attr->mode; 245 246 if (attr == &dev_attr_als_channel.attr || 247 attr == &dev_attr_als_en.attr) { 248 if (!bl->lm3533->have_als) 249 mode = 0; 250 } 251 252 return mode; 253 }; 254 255 static struct attribute_group lm3533_bl_attribute_group = { 256 .is_visible = lm3533_bl_attr_is_visible, 257 .attrs = lm3533_bl_attributes 258 }; 259 260 static int lm3533_bl_setup(struct lm3533_bl *bl, 261 struct lm3533_bl_platform_data *pdata) 262 { 263 int ret; 264 265 ret = lm3533_ctrlbank_set_max_current(&bl->cb, pdata->max_current); 266 if (ret) 267 return ret; 268 269 return lm3533_ctrlbank_set_pwm(&bl->cb, pdata->pwm); 270 } 271 272 static int lm3533_bl_probe(struct platform_device *pdev) 273 { 274 struct lm3533 *lm3533; 275 struct lm3533_bl_platform_data *pdata; 276 struct lm3533_bl *bl; 277 struct backlight_device *bd; 278 struct backlight_properties props; 279 int ret; 280 281 dev_dbg(&pdev->dev, "%s\n", __func__); 282 283 lm3533 = dev_get_drvdata(pdev->dev.parent); 284 if (!lm3533) 285 return -EINVAL; 286 287 pdata = pdev->dev.platform_data; 288 if (!pdata) { 289 dev_err(&pdev->dev, "no platform data\n"); 290 return -EINVAL; 291 } 292 293 if (pdev->id < 0 || pdev->id >= LM3533_HVCTRLBANK_COUNT) { 294 dev_err(&pdev->dev, "illegal backlight id %d\n", pdev->id); 295 return -EINVAL; 296 } 297 298 bl = devm_kzalloc(&pdev->dev, sizeof(*bl), GFP_KERNEL); 299 if (!bl) { 300 dev_err(&pdev->dev, 301 "failed to allocate memory for backlight\n"); 302 return -ENOMEM; 303 } 304 305 bl->lm3533 = lm3533; 306 bl->id = pdev->id; 307 308 bl->cb.lm3533 = lm3533; 309 bl->cb.id = lm3533_bl_get_ctrlbank_id(bl); 310 bl->cb.dev = NULL; /* until registered */ 311 312 memset(&props, 0, sizeof(props)); 313 props.type = BACKLIGHT_RAW; 314 props.max_brightness = LM3533_BL_MAX_BRIGHTNESS; 315 props.brightness = pdata->default_brightness; 316 bd = backlight_device_register(pdata->name, pdev->dev.parent, bl, 317 &lm3533_bl_ops, &props); 318 if (IS_ERR(bd)) { 319 dev_err(&pdev->dev, "failed to register backlight device\n"); 320 return PTR_ERR(bd); 321 } 322 323 bl->bd = bd; 324 bl->cb.dev = &bl->bd->dev; 325 326 platform_set_drvdata(pdev, bl); 327 328 ret = sysfs_create_group(&bd->dev.kobj, &lm3533_bl_attribute_group); 329 if (ret < 0) { 330 dev_err(&pdev->dev, "failed to create sysfs attributes\n"); 331 goto err_unregister; 332 } 333 334 backlight_update_status(bd); 335 336 ret = lm3533_bl_setup(bl, pdata); 337 if (ret) 338 goto err_sysfs_remove; 339 340 ret = lm3533_ctrlbank_enable(&bl->cb); 341 if (ret) 342 goto err_sysfs_remove; 343 344 return 0; 345 346 err_sysfs_remove: 347 sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group); 348 err_unregister: 349 backlight_device_unregister(bd); 350 351 return ret; 352 } 353 354 static int lm3533_bl_remove(struct platform_device *pdev) 355 { 356 struct lm3533_bl *bl = platform_get_drvdata(pdev); 357 struct backlight_device *bd = bl->bd; 358 359 dev_dbg(&bd->dev, "%s\n", __func__); 360 361 bd->props.power = FB_BLANK_POWERDOWN; 362 bd->props.brightness = 0; 363 364 lm3533_ctrlbank_disable(&bl->cb); 365 sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group); 366 backlight_device_unregister(bd); 367 368 return 0; 369 } 370 371 #ifdef CONFIG_PM 372 static int lm3533_bl_suspend(struct platform_device *pdev, pm_message_t state) 373 { 374 struct lm3533_bl *bl = platform_get_drvdata(pdev); 375 376 dev_dbg(&pdev->dev, "%s\n", __func__); 377 378 return lm3533_ctrlbank_disable(&bl->cb); 379 } 380 381 static int lm3533_bl_resume(struct platform_device *pdev) 382 { 383 struct lm3533_bl *bl = platform_get_drvdata(pdev); 384 385 dev_dbg(&pdev->dev, "%s\n", __func__); 386 387 return lm3533_ctrlbank_enable(&bl->cb); 388 } 389 #else 390 #define lm3533_bl_suspend NULL 391 #define lm3533_bl_resume NULL 392 #endif 393 394 static void lm3533_bl_shutdown(struct platform_device *pdev) 395 { 396 struct lm3533_bl *bl = platform_get_drvdata(pdev); 397 398 dev_dbg(&pdev->dev, "%s\n", __func__); 399 400 lm3533_ctrlbank_disable(&bl->cb); 401 } 402 403 static struct platform_driver lm3533_bl_driver = { 404 .driver = { 405 .name = "lm3533-backlight", 406 .owner = THIS_MODULE, 407 }, 408 .probe = lm3533_bl_probe, 409 .remove = lm3533_bl_remove, 410 .shutdown = lm3533_bl_shutdown, 411 .suspend = lm3533_bl_suspend, 412 .resume = lm3533_bl_resume, 413 }; 414 module_platform_driver(lm3533_bl_driver); 415 416 MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>"); 417 MODULE_DESCRIPTION("LM3533 Backlight driver"); 418 MODULE_LICENSE("GPL"); 419 MODULE_ALIAS("platform:lm3533-backlight"); 420