12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 27f26c970SJohan Hovold /* 37f26c970SJohan Hovold * lm3533-bl.c -- LM3533 Backlight driver 47f26c970SJohan Hovold * 57f26c970SJohan Hovold * Copyright (C) 2011-2012 Texas Instruments 67f26c970SJohan Hovold * 77f26c970SJohan Hovold * Author: Johan Hovold <jhovold@gmail.com> 87f26c970SJohan Hovold */ 97f26c970SJohan Hovold 107f26c970SJohan Hovold #include <linux/module.h> 117f26c970SJohan Hovold #include <linux/init.h> 127f26c970SJohan Hovold #include <linux/platform_device.h> 137f26c970SJohan Hovold #include <linux/backlight.h> 147f26c970SJohan Hovold #include <linux/fb.h> 157f26c970SJohan Hovold #include <linux/slab.h> 167f26c970SJohan Hovold 177f26c970SJohan Hovold #include <linux/mfd/lm3533.h> 187f26c970SJohan Hovold 197f26c970SJohan Hovold 207f26c970SJohan Hovold #define LM3533_HVCTRLBANK_COUNT 2 217f26c970SJohan Hovold #define LM3533_BL_MAX_BRIGHTNESS 255 227f26c970SJohan Hovold 237f26c970SJohan Hovold #define LM3533_REG_CTRLBANK_AB_BCONF 0x1a 247f26c970SJohan Hovold 257f26c970SJohan Hovold 267f26c970SJohan Hovold struct lm3533_bl { 277f26c970SJohan Hovold struct lm3533 *lm3533; 287f26c970SJohan Hovold struct lm3533_ctrlbank cb; 297f26c970SJohan Hovold struct backlight_device *bd; 307f26c970SJohan Hovold int id; 317f26c970SJohan Hovold }; 327f26c970SJohan Hovold 337f26c970SJohan Hovold 347f26c970SJohan Hovold static inline int lm3533_bl_get_ctrlbank_id(struct lm3533_bl *bl) 357f26c970SJohan Hovold { 367f26c970SJohan Hovold return bl->id; 377f26c970SJohan Hovold } 387f26c970SJohan Hovold 397f26c970SJohan Hovold static int lm3533_bl_update_status(struct backlight_device *bd) 407f26c970SJohan Hovold { 417f26c970SJohan Hovold struct lm3533_bl *bl = bl_get_data(bd); 427f26c970SJohan Hovold 43*51d53e5bSSam Ravnborg return lm3533_ctrlbank_set_brightness(&bl->cb, backlight_get_brightness(bd)); 447f26c970SJohan Hovold } 457f26c970SJohan Hovold 467f26c970SJohan Hovold static int lm3533_bl_get_brightness(struct backlight_device *bd) 477f26c970SJohan Hovold { 487f26c970SJohan Hovold struct lm3533_bl *bl = bl_get_data(bd); 497f26c970SJohan Hovold u8 val; 507f26c970SJohan Hovold int ret; 517f26c970SJohan Hovold 527f26c970SJohan Hovold ret = lm3533_ctrlbank_get_brightness(&bl->cb, &val); 537f26c970SJohan Hovold if (ret) 547f26c970SJohan Hovold return ret; 557f26c970SJohan Hovold 567f26c970SJohan Hovold return val; 577f26c970SJohan Hovold } 587f26c970SJohan Hovold 597f26c970SJohan Hovold static const struct backlight_ops lm3533_bl_ops = { 607f26c970SJohan Hovold .get_brightness = lm3533_bl_get_brightness, 617f26c970SJohan Hovold .update_status = lm3533_bl_update_status, 627f26c970SJohan Hovold }; 637f26c970SJohan Hovold 647f26c970SJohan Hovold static ssize_t show_id(struct device *dev, 657f26c970SJohan Hovold struct device_attribute *attr, char *buf) 667f26c970SJohan Hovold { 677f26c970SJohan Hovold struct lm3533_bl *bl = dev_get_drvdata(dev); 687f26c970SJohan Hovold 697f26c970SJohan Hovold return scnprintf(buf, PAGE_SIZE, "%d\n", bl->id); 707f26c970SJohan Hovold } 717f26c970SJohan Hovold 727f26c970SJohan Hovold static ssize_t show_als_channel(struct device *dev, 737f26c970SJohan Hovold struct device_attribute *attr, char *buf) 747f26c970SJohan Hovold { 757f26c970SJohan Hovold struct lm3533_bl *bl = dev_get_drvdata(dev); 767f26c970SJohan Hovold unsigned channel = lm3533_bl_get_ctrlbank_id(bl); 777f26c970SJohan Hovold 787f26c970SJohan Hovold return scnprintf(buf, PAGE_SIZE, "%u\n", channel); 797f26c970SJohan Hovold } 807f26c970SJohan Hovold 817f26c970SJohan Hovold static ssize_t show_als_en(struct device *dev, 827f26c970SJohan Hovold struct device_attribute *attr, char *buf) 837f26c970SJohan Hovold { 847f26c970SJohan Hovold struct lm3533_bl *bl = dev_get_drvdata(dev); 857f26c970SJohan Hovold int ctrlbank = lm3533_bl_get_ctrlbank_id(bl); 867f26c970SJohan Hovold u8 val; 877f26c970SJohan Hovold u8 mask; 887f26c970SJohan Hovold bool enable; 897f26c970SJohan Hovold int ret; 907f26c970SJohan Hovold 917f26c970SJohan Hovold ret = lm3533_read(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, &val); 927f26c970SJohan Hovold if (ret) 937f26c970SJohan Hovold return ret; 947f26c970SJohan Hovold 957f26c970SJohan Hovold mask = 1 << (2 * ctrlbank); 967f26c970SJohan Hovold enable = val & mask; 977f26c970SJohan Hovold 987f26c970SJohan Hovold return scnprintf(buf, PAGE_SIZE, "%d\n", enable); 997f26c970SJohan Hovold } 1007f26c970SJohan Hovold 1017f26c970SJohan Hovold static ssize_t store_als_en(struct device *dev, 1027f26c970SJohan Hovold struct device_attribute *attr, 1037f26c970SJohan Hovold const char *buf, size_t len) 1047f26c970SJohan Hovold { 1057f26c970SJohan Hovold struct lm3533_bl *bl = dev_get_drvdata(dev); 1067f26c970SJohan Hovold int ctrlbank = lm3533_bl_get_ctrlbank_id(bl); 1077f26c970SJohan Hovold int enable; 1087f26c970SJohan Hovold u8 val; 1097f26c970SJohan Hovold u8 mask; 1107f26c970SJohan Hovold int ret; 1117f26c970SJohan Hovold 1127f26c970SJohan Hovold if (kstrtoint(buf, 0, &enable)) 1137f26c970SJohan Hovold return -EINVAL; 1147f26c970SJohan Hovold 1157f26c970SJohan Hovold mask = 1 << (2 * ctrlbank); 1167f26c970SJohan Hovold 1177f26c970SJohan Hovold if (enable) 1187f26c970SJohan Hovold val = mask; 1197f26c970SJohan Hovold else 1207f26c970SJohan Hovold val = 0; 1217f26c970SJohan Hovold 1227f26c970SJohan Hovold ret = lm3533_update(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, val, 1237f26c970SJohan Hovold mask); 1247f26c970SJohan Hovold if (ret) 1257f26c970SJohan Hovold return ret; 1267f26c970SJohan Hovold 1277f26c970SJohan Hovold return len; 1287f26c970SJohan Hovold } 1297f26c970SJohan Hovold 1307f26c970SJohan Hovold static ssize_t show_linear(struct device *dev, 1317f26c970SJohan Hovold struct device_attribute *attr, char *buf) 1327f26c970SJohan Hovold { 1337f26c970SJohan Hovold struct lm3533_bl *bl = dev_get_drvdata(dev); 1347f26c970SJohan Hovold u8 val; 1357f26c970SJohan Hovold u8 mask; 1367f26c970SJohan Hovold int linear; 1377f26c970SJohan Hovold int ret; 1387f26c970SJohan Hovold 1397f26c970SJohan Hovold ret = lm3533_read(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, &val); 1407f26c970SJohan Hovold if (ret) 1417f26c970SJohan Hovold return ret; 1427f26c970SJohan Hovold 1437f26c970SJohan Hovold mask = 1 << (2 * lm3533_bl_get_ctrlbank_id(bl) + 1); 1447f26c970SJohan Hovold 1457f26c970SJohan Hovold if (val & mask) 1467f26c970SJohan Hovold linear = 1; 1477f26c970SJohan Hovold else 1487f26c970SJohan Hovold linear = 0; 1497f26c970SJohan Hovold 1507f26c970SJohan Hovold return scnprintf(buf, PAGE_SIZE, "%x\n", linear); 1517f26c970SJohan Hovold } 1527f26c970SJohan Hovold 1537f26c970SJohan Hovold static ssize_t store_linear(struct device *dev, 1547f26c970SJohan Hovold struct device_attribute *attr, 1557f26c970SJohan Hovold const char *buf, size_t len) 1567f26c970SJohan Hovold { 1577f26c970SJohan Hovold struct lm3533_bl *bl = dev_get_drvdata(dev); 1587f26c970SJohan Hovold unsigned long linear; 1597f26c970SJohan Hovold u8 mask; 1607f26c970SJohan Hovold u8 val; 1617f26c970SJohan Hovold int ret; 1627f26c970SJohan Hovold 1637f26c970SJohan Hovold if (kstrtoul(buf, 0, &linear)) 1647f26c970SJohan Hovold return -EINVAL; 1657f26c970SJohan Hovold 1667f26c970SJohan Hovold mask = 1 << (2 * lm3533_bl_get_ctrlbank_id(bl) + 1); 1677f26c970SJohan Hovold 1687f26c970SJohan Hovold if (linear) 1697f26c970SJohan Hovold val = mask; 1707f26c970SJohan Hovold else 1717f26c970SJohan Hovold val = 0; 1727f26c970SJohan Hovold 1737f26c970SJohan Hovold ret = lm3533_update(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, val, 1747f26c970SJohan Hovold mask); 1757f26c970SJohan Hovold if (ret) 1767f26c970SJohan Hovold return ret; 1777f26c970SJohan Hovold 1787f26c970SJohan Hovold return len; 1797f26c970SJohan Hovold } 1807f26c970SJohan Hovold 1817f26c970SJohan Hovold static ssize_t show_pwm(struct device *dev, 1827f26c970SJohan Hovold struct device_attribute *attr, 1837f26c970SJohan Hovold char *buf) 1847f26c970SJohan Hovold { 1857f26c970SJohan Hovold struct lm3533_bl *bl = dev_get_drvdata(dev); 1867f26c970SJohan Hovold u8 val; 1877f26c970SJohan Hovold int ret; 1887f26c970SJohan Hovold 1897f26c970SJohan Hovold ret = lm3533_ctrlbank_get_pwm(&bl->cb, &val); 1907f26c970SJohan Hovold if (ret) 1917f26c970SJohan Hovold return ret; 1927f26c970SJohan Hovold 1937f26c970SJohan Hovold return scnprintf(buf, PAGE_SIZE, "%u\n", val); 1947f26c970SJohan Hovold } 1957f26c970SJohan Hovold 1967f26c970SJohan Hovold static ssize_t store_pwm(struct device *dev, 1977f26c970SJohan Hovold struct device_attribute *attr, 1987f26c970SJohan Hovold const char *buf, size_t len) 1997f26c970SJohan Hovold { 2007f26c970SJohan Hovold struct lm3533_bl *bl = dev_get_drvdata(dev); 2017f26c970SJohan Hovold u8 val; 2027f26c970SJohan Hovold int ret; 2037f26c970SJohan Hovold 2047f26c970SJohan Hovold if (kstrtou8(buf, 0, &val)) 2057f26c970SJohan Hovold return -EINVAL; 2067f26c970SJohan Hovold 2077f26c970SJohan Hovold ret = lm3533_ctrlbank_set_pwm(&bl->cb, val); 2087f26c970SJohan Hovold if (ret) 2097f26c970SJohan Hovold return ret; 2107f26c970SJohan Hovold 2117f26c970SJohan Hovold return len; 2127f26c970SJohan Hovold } 2137f26c970SJohan Hovold 2147f26c970SJohan Hovold static LM3533_ATTR_RO(als_channel); 2157f26c970SJohan Hovold static LM3533_ATTR_RW(als_en); 2167f26c970SJohan Hovold static LM3533_ATTR_RO(id); 2177f26c970SJohan Hovold static LM3533_ATTR_RW(linear); 2187f26c970SJohan Hovold static LM3533_ATTR_RW(pwm); 2197f26c970SJohan Hovold 2207f26c970SJohan Hovold static struct attribute *lm3533_bl_attributes[] = { 2217f26c970SJohan Hovold &dev_attr_als_channel.attr, 2227f26c970SJohan Hovold &dev_attr_als_en.attr, 2237f26c970SJohan Hovold &dev_attr_id.attr, 2247f26c970SJohan Hovold &dev_attr_linear.attr, 2257f26c970SJohan Hovold &dev_attr_pwm.attr, 2267f26c970SJohan Hovold NULL, 2277f26c970SJohan Hovold }; 2287f26c970SJohan Hovold 2297f26c970SJohan Hovold static umode_t lm3533_bl_attr_is_visible(struct kobject *kobj, 2307f26c970SJohan Hovold struct attribute *attr, int n) 2317f26c970SJohan Hovold { 232a9b9b2afSWang Qing struct device *dev = kobj_to_dev(kobj); 2337f26c970SJohan Hovold struct lm3533_bl *bl = dev_get_drvdata(dev); 2347f26c970SJohan Hovold umode_t mode = attr->mode; 2357f26c970SJohan Hovold 2367f26c970SJohan Hovold if (attr == &dev_attr_als_channel.attr || 2377f26c970SJohan Hovold attr == &dev_attr_als_en.attr) { 2387f26c970SJohan Hovold if (!bl->lm3533->have_als) 2397f26c970SJohan Hovold mode = 0; 2407f26c970SJohan Hovold } 2417f26c970SJohan Hovold 2427f26c970SJohan Hovold return mode; 2437f26c970SJohan Hovold }; 2447f26c970SJohan Hovold 2457f26c970SJohan Hovold static struct attribute_group lm3533_bl_attribute_group = { 2467f26c970SJohan Hovold .is_visible = lm3533_bl_attr_is_visible, 2477f26c970SJohan Hovold .attrs = lm3533_bl_attributes 2487f26c970SJohan Hovold }; 2497f26c970SJohan Hovold 2501b9e450dSBill Pemberton static int lm3533_bl_setup(struct lm3533_bl *bl, 2517f26c970SJohan Hovold struct lm3533_bl_platform_data *pdata) 2527f26c970SJohan Hovold { 2537f26c970SJohan Hovold int ret; 2547f26c970SJohan Hovold 2557f26c970SJohan Hovold ret = lm3533_ctrlbank_set_max_current(&bl->cb, pdata->max_current); 2567f26c970SJohan Hovold if (ret) 2577f26c970SJohan Hovold return ret; 2587f26c970SJohan Hovold 2597f26c970SJohan Hovold return lm3533_ctrlbank_set_pwm(&bl->cb, pdata->pwm); 2607f26c970SJohan Hovold } 2617f26c970SJohan Hovold 2621b9e450dSBill Pemberton static int lm3533_bl_probe(struct platform_device *pdev) 2637f26c970SJohan Hovold { 2647f26c970SJohan Hovold struct lm3533 *lm3533; 2657f26c970SJohan Hovold struct lm3533_bl_platform_data *pdata; 2667f26c970SJohan Hovold struct lm3533_bl *bl; 2677f26c970SJohan Hovold struct backlight_device *bd; 2687f26c970SJohan Hovold struct backlight_properties props; 2697f26c970SJohan Hovold int ret; 2707f26c970SJohan Hovold 2717f26c970SJohan Hovold dev_dbg(&pdev->dev, "%s\n", __func__); 2727f26c970SJohan Hovold 2737f26c970SJohan Hovold lm3533 = dev_get_drvdata(pdev->dev.parent); 2747f26c970SJohan Hovold if (!lm3533) 2757f26c970SJohan Hovold return -EINVAL; 2767f26c970SJohan Hovold 277c512794cSJingoo Han pdata = dev_get_platdata(&pdev->dev); 2787f26c970SJohan Hovold if (!pdata) { 2797f26c970SJohan Hovold dev_err(&pdev->dev, "no platform data\n"); 2807f26c970SJohan Hovold return -EINVAL; 2817f26c970SJohan Hovold } 2827f26c970SJohan Hovold 2837f26c970SJohan Hovold if (pdev->id < 0 || pdev->id >= LM3533_HVCTRLBANK_COUNT) { 2847f26c970SJohan Hovold dev_err(&pdev->dev, "illegal backlight id %d\n", pdev->id); 2857f26c970SJohan Hovold return -EINVAL; 2867f26c970SJohan Hovold } 2877f26c970SJohan Hovold 288b4a74615SJingoo Han bl = devm_kzalloc(&pdev->dev, sizeof(*bl), GFP_KERNEL); 28944d516f9SJingoo Han if (!bl) 2907f26c970SJohan Hovold return -ENOMEM; 2917f26c970SJohan Hovold 2927f26c970SJohan Hovold bl->lm3533 = lm3533; 2937f26c970SJohan Hovold bl->id = pdev->id; 2947f26c970SJohan Hovold 2957f26c970SJohan Hovold bl->cb.lm3533 = lm3533; 2967f26c970SJohan Hovold bl->cb.id = lm3533_bl_get_ctrlbank_id(bl); 2977f26c970SJohan Hovold bl->cb.dev = NULL; /* until registered */ 2987f26c970SJohan Hovold 2997f26c970SJohan Hovold memset(&props, 0, sizeof(props)); 3007f26c970SJohan Hovold props.type = BACKLIGHT_RAW; 3017f26c970SJohan Hovold props.max_brightness = LM3533_BL_MAX_BRIGHTNESS; 3027f26c970SJohan Hovold props.brightness = pdata->default_brightness; 303e86c7098SJingoo Han bd = devm_backlight_device_register(&pdev->dev, pdata->name, 304e86c7098SJingoo Han pdev->dev.parent, bl, &lm3533_bl_ops, 305e86c7098SJingoo Han &props); 3067f26c970SJohan Hovold if (IS_ERR(bd)) { 3077f26c970SJohan Hovold dev_err(&pdev->dev, "failed to register backlight device\n"); 308b4a74615SJingoo Han return PTR_ERR(bd); 3097f26c970SJohan Hovold } 3107f26c970SJohan Hovold 3117f26c970SJohan Hovold bl->bd = bd; 3127f26c970SJohan Hovold bl->cb.dev = &bl->bd->dev; 3137f26c970SJohan Hovold 3147f26c970SJohan Hovold platform_set_drvdata(pdev, bl); 3157f26c970SJohan Hovold 3167f26c970SJohan Hovold ret = sysfs_create_group(&bd->dev.kobj, &lm3533_bl_attribute_group); 3177f26c970SJohan Hovold if (ret < 0) { 3187f26c970SJohan Hovold dev_err(&pdev->dev, "failed to create sysfs attributes\n"); 319e86c7098SJingoo Han return ret; 3207f26c970SJohan Hovold } 3217f26c970SJohan Hovold 3227f26c970SJohan Hovold backlight_update_status(bd); 3237f26c970SJohan Hovold 3247f26c970SJohan Hovold ret = lm3533_bl_setup(bl, pdata); 3257f26c970SJohan Hovold if (ret) 3267f26c970SJohan Hovold goto err_sysfs_remove; 3277f26c970SJohan Hovold 3287f26c970SJohan Hovold ret = lm3533_ctrlbank_enable(&bl->cb); 3297f26c970SJohan Hovold if (ret) 3307f26c970SJohan Hovold goto err_sysfs_remove; 3317f26c970SJohan Hovold 3327f26c970SJohan Hovold return 0; 3337f26c970SJohan Hovold 3347f26c970SJohan Hovold err_sysfs_remove: 3357f26c970SJohan Hovold sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group); 3367f26c970SJohan Hovold 3377f26c970SJohan Hovold return ret; 3387f26c970SJohan Hovold } 3397f26c970SJohan Hovold 3407e4b9d0bSBill Pemberton static int lm3533_bl_remove(struct platform_device *pdev) 3417f26c970SJohan Hovold { 3427f26c970SJohan Hovold struct lm3533_bl *bl = platform_get_drvdata(pdev); 3437f26c970SJohan Hovold struct backlight_device *bd = bl->bd; 3447f26c970SJohan Hovold 3457f26c970SJohan Hovold dev_dbg(&bd->dev, "%s\n", __func__); 3467f26c970SJohan Hovold 3477f26c970SJohan Hovold bd->props.power = FB_BLANK_POWERDOWN; 3487f26c970SJohan Hovold bd->props.brightness = 0; 3497f26c970SJohan Hovold 3507f26c970SJohan Hovold lm3533_ctrlbank_disable(&bl->cb); 3517f26c970SJohan Hovold sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group); 3527f26c970SJohan Hovold 3537f26c970SJohan Hovold return 0; 3547f26c970SJohan Hovold } 3557f26c970SJohan Hovold 356b0792962SJingoo Han #ifdef CONFIG_PM_SLEEP 357b0792962SJingoo Han static int lm3533_bl_suspend(struct device *dev) 3587f26c970SJohan Hovold { 359b0792962SJingoo Han struct lm3533_bl *bl = dev_get_drvdata(dev); 3607f26c970SJohan Hovold 361b0792962SJingoo Han dev_dbg(dev, "%s\n", __func__); 3627f26c970SJohan Hovold 3637f26c970SJohan Hovold return lm3533_ctrlbank_disable(&bl->cb); 3647f26c970SJohan Hovold } 3657f26c970SJohan Hovold 366b0792962SJingoo Han static int lm3533_bl_resume(struct device *dev) 3677f26c970SJohan Hovold { 368b0792962SJingoo Han struct lm3533_bl *bl = dev_get_drvdata(dev); 3697f26c970SJohan Hovold 370b0792962SJingoo Han dev_dbg(dev, "%s\n", __func__); 3717f26c970SJohan Hovold 3727f26c970SJohan Hovold return lm3533_ctrlbank_enable(&bl->cb); 3737f26c970SJohan Hovold } 3747f26c970SJohan Hovold #endif 3757f26c970SJohan Hovold 376b0792962SJingoo Han static SIMPLE_DEV_PM_OPS(lm3533_bl_pm_ops, lm3533_bl_suspend, lm3533_bl_resume); 377b0792962SJingoo Han 3787f26c970SJohan Hovold static void lm3533_bl_shutdown(struct platform_device *pdev) 3797f26c970SJohan Hovold { 3807f26c970SJohan Hovold struct lm3533_bl *bl = platform_get_drvdata(pdev); 3817f26c970SJohan Hovold 3827f26c970SJohan Hovold dev_dbg(&pdev->dev, "%s\n", __func__); 3837f26c970SJohan Hovold 3847f26c970SJohan Hovold lm3533_ctrlbank_disable(&bl->cb); 3857f26c970SJohan Hovold } 3867f26c970SJohan Hovold 3877f26c970SJohan Hovold static struct platform_driver lm3533_bl_driver = { 3887f26c970SJohan Hovold .driver = { 3897f26c970SJohan Hovold .name = "lm3533-backlight", 390b0792962SJingoo Han .pm = &lm3533_bl_pm_ops, 3917f26c970SJohan Hovold }, 3927f26c970SJohan Hovold .probe = lm3533_bl_probe, 393d1723fa2SBill Pemberton .remove = lm3533_bl_remove, 3947f26c970SJohan Hovold .shutdown = lm3533_bl_shutdown, 3957f26c970SJohan Hovold }; 3967f26c970SJohan Hovold module_platform_driver(lm3533_bl_driver); 3977f26c970SJohan Hovold 3987f26c970SJohan Hovold MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>"); 3997f26c970SJohan Hovold MODULE_DESCRIPTION("LM3533 Backlight driver"); 4007f26c970SJohan Hovold MODULE_LICENSE("GPL"); 4017f26c970SJohan Hovold MODULE_ALIAS("platform:lm3533-backlight"); 402