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