1*2aec85b2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2eebfdc17SMatthias Kaehlcke /* 3eebfdc17SMatthias Kaehlcke * tps65217_bl.c 4eebfdc17SMatthias Kaehlcke * 5eebfdc17SMatthias Kaehlcke * TPS65217 backlight driver 6eebfdc17SMatthias Kaehlcke * 7eebfdc17SMatthias Kaehlcke * Copyright (C) 2012 Matthias Kaehlcke 8eebfdc17SMatthias Kaehlcke * Author: Matthias Kaehlcke <matthias@kaehlcke.net> 9eebfdc17SMatthias Kaehlcke */ 10eebfdc17SMatthias Kaehlcke 11eebfdc17SMatthias Kaehlcke #include <linux/kernel.h> 12eebfdc17SMatthias Kaehlcke #include <linux/backlight.h> 13eebfdc17SMatthias Kaehlcke #include <linux/err.h> 14eebfdc17SMatthias Kaehlcke #include <linux/fb.h> 15eebfdc17SMatthias Kaehlcke #include <linux/mfd/tps65217.h> 16eebfdc17SMatthias Kaehlcke #include <linux/module.h> 17eebfdc17SMatthias Kaehlcke #include <linux/platform_device.h> 18eebfdc17SMatthias Kaehlcke #include <linux/slab.h> 19eebfdc17SMatthias Kaehlcke 20eebfdc17SMatthias Kaehlcke struct tps65217_bl { 21eebfdc17SMatthias Kaehlcke struct tps65217 *tps; 22eebfdc17SMatthias Kaehlcke struct device *dev; 23eebfdc17SMatthias Kaehlcke struct backlight_device *bl; 24eebfdc17SMatthias Kaehlcke bool is_enabled; 25eebfdc17SMatthias Kaehlcke }; 26eebfdc17SMatthias Kaehlcke 27eebfdc17SMatthias Kaehlcke static int tps65217_bl_enable(struct tps65217_bl *tps65217_bl) 28eebfdc17SMatthias Kaehlcke { 29eebfdc17SMatthias Kaehlcke int rc; 30eebfdc17SMatthias Kaehlcke 31eebfdc17SMatthias Kaehlcke rc = tps65217_set_bits(tps65217_bl->tps, TPS65217_REG_WLEDCTRL1, 32eebfdc17SMatthias Kaehlcke TPS65217_WLEDCTRL1_ISINK_ENABLE, 33eebfdc17SMatthias Kaehlcke TPS65217_WLEDCTRL1_ISINK_ENABLE, TPS65217_PROTECT_NONE); 34eebfdc17SMatthias Kaehlcke if (rc) { 35eebfdc17SMatthias Kaehlcke dev_err(tps65217_bl->dev, 36eebfdc17SMatthias Kaehlcke "failed to enable backlight: %d\n", rc); 37eebfdc17SMatthias Kaehlcke return rc; 38eebfdc17SMatthias Kaehlcke } 39eebfdc17SMatthias Kaehlcke 40eebfdc17SMatthias Kaehlcke tps65217_bl->is_enabled = true; 41eebfdc17SMatthias Kaehlcke 42eebfdc17SMatthias Kaehlcke dev_dbg(tps65217_bl->dev, "backlight enabled\n"); 43eebfdc17SMatthias Kaehlcke 44eebfdc17SMatthias Kaehlcke return 0; 45eebfdc17SMatthias Kaehlcke } 46eebfdc17SMatthias Kaehlcke 47eebfdc17SMatthias Kaehlcke static int tps65217_bl_disable(struct tps65217_bl *tps65217_bl) 48eebfdc17SMatthias Kaehlcke { 49eebfdc17SMatthias Kaehlcke int rc; 50eebfdc17SMatthias Kaehlcke 51eebfdc17SMatthias Kaehlcke rc = tps65217_clear_bits(tps65217_bl->tps, 52eebfdc17SMatthias Kaehlcke TPS65217_REG_WLEDCTRL1, 53eebfdc17SMatthias Kaehlcke TPS65217_WLEDCTRL1_ISINK_ENABLE, 54eebfdc17SMatthias Kaehlcke TPS65217_PROTECT_NONE); 55eebfdc17SMatthias Kaehlcke if (rc) { 56eebfdc17SMatthias Kaehlcke dev_err(tps65217_bl->dev, 57eebfdc17SMatthias Kaehlcke "failed to disable backlight: %d\n", rc); 58eebfdc17SMatthias Kaehlcke return rc; 59eebfdc17SMatthias Kaehlcke } 60eebfdc17SMatthias Kaehlcke 61eebfdc17SMatthias Kaehlcke tps65217_bl->is_enabled = false; 62eebfdc17SMatthias Kaehlcke 63eebfdc17SMatthias Kaehlcke dev_dbg(tps65217_bl->dev, "backlight disabled\n"); 64eebfdc17SMatthias Kaehlcke 65eebfdc17SMatthias Kaehlcke return 0; 66eebfdc17SMatthias Kaehlcke } 67eebfdc17SMatthias Kaehlcke 68eebfdc17SMatthias Kaehlcke static int tps65217_bl_update_status(struct backlight_device *bl) 69eebfdc17SMatthias Kaehlcke { 70eebfdc17SMatthias Kaehlcke struct tps65217_bl *tps65217_bl = bl_get_data(bl); 71eebfdc17SMatthias Kaehlcke int rc; 7251d53e5bSSam Ravnborg int brightness = backlight_get_brightness(bl); 73eebfdc17SMatthias Kaehlcke 74eebfdc17SMatthias Kaehlcke if (brightness > 0) { 75eebfdc17SMatthias Kaehlcke rc = tps65217_reg_write(tps65217_bl->tps, 76eebfdc17SMatthias Kaehlcke TPS65217_REG_WLEDCTRL2, 77eebfdc17SMatthias Kaehlcke brightness - 1, 78eebfdc17SMatthias Kaehlcke TPS65217_PROTECT_NONE); 79eebfdc17SMatthias Kaehlcke if (rc) { 80eebfdc17SMatthias Kaehlcke dev_err(tps65217_bl->dev, 81eebfdc17SMatthias Kaehlcke "failed to set brightness level: %d\n", rc); 82eebfdc17SMatthias Kaehlcke return rc; 83eebfdc17SMatthias Kaehlcke } 84eebfdc17SMatthias Kaehlcke 85eebfdc17SMatthias Kaehlcke dev_dbg(tps65217_bl->dev, "brightness set to %d\n", brightness); 86eebfdc17SMatthias Kaehlcke 87eebfdc17SMatthias Kaehlcke if (!tps65217_bl->is_enabled) 88eebfdc17SMatthias Kaehlcke rc = tps65217_bl_enable(tps65217_bl); 89eebfdc17SMatthias Kaehlcke } else { 90eebfdc17SMatthias Kaehlcke rc = tps65217_bl_disable(tps65217_bl); 91eebfdc17SMatthias Kaehlcke } 92eebfdc17SMatthias Kaehlcke 93eebfdc17SMatthias Kaehlcke return rc; 94eebfdc17SMatthias Kaehlcke } 95eebfdc17SMatthias Kaehlcke 96eebfdc17SMatthias Kaehlcke static const struct backlight_ops tps65217_bl_ops = { 97eebfdc17SMatthias Kaehlcke .options = BL_CORE_SUSPENDRESUME, 98eebfdc17SMatthias Kaehlcke .update_status = tps65217_bl_update_status, 99eebfdc17SMatthias Kaehlcke }; 100eebfdc17SMatthias Kaehlcke 101eebfdc17SMatthias Kaehlcke static int tps65217_bl_hw_init(struct tps65217_bl *tps65217_bl, 102eebfdc17SMatthias Kaehlcke struct tps65217_bl_pdata *pdata) 103eebfdc17SMatthias Kaehlcke { 104eebfdc17SMatthias Kaehlcke int rc; 105eebfdc17SMatthias Kaehlcke 106eebfdc17SMatthias Kaehlcke rc = tps65217_bl_disable(tps65217_bl); 107eebfdc17SMatthias Kaehlcke if (rc) 108eebfdc17SMatthias Kaehlcke return rc; 109eebfdc17SMatthias Kaehlcke 110eebfdc17SMatthias Kaehlcke switch (pdata->isel) { 111eebfdc17SMatthias Kaehlcke case TPS65217_BL_ISET1: 112eebfdc17SMatthias Kaehlcke /* select ISET_1 current level */ 113eebfdc17SMatthias Kaehlcke rc = tps65217_clear_bits(tps65217_bl->tps, 114eebfdc17SMatthias Kaehlcke TPS65217_REG_WLEDCTRL1, 115eebfdc17SMatthias Kaehlcke TPS65217_WLEDCTRL1_ISEL, 116eebfdc17SMatthias Kaehlcke TPS65217_PROTECT_NONE); 117eebfdc17SMatthias Kaehlcke if (rc) { 118eebfdc17SMatthias Kaehlcke dev_err(tps65217_bl->dev, 119eebfdc17SMatthias Kaehlcke "failed to select ISET1 current level: %d)\n", 120eebfdc17SMatthias Kaehlcke rc); 121eebfdc17SMatthias Kaehlcke return rc; 122eebfdc17SMatthias Kaehlcke } 123eebfdc17SMatthias Kaehlcke 124eebfdc17SMatthias Kaehlcke dev_dbg(tps65217_bl->dev, "selected ISET1 current level\n"); 125eebfdc17SMatthias Kaehlcke 126eebfdc17SMatthias Kaehlcke break; 127eebfdc17SMatthias Kaehlcke 128eebfdc17SMatthias Kaehlcke case TPS65217_BL_ISET2: 129eebfdc17SMatthias Kaehlcke /* select ISET2 current level */ 130eebfdc17SMatthias Kaehlcke rc = tps65217_set_bits(tps65217_bl->tps, TPS65217_REG_WLEDCTRL1, 131eebfdc17SMatthias Kaehlcke TPS65217_WLEDCTRL1_ISEL, 132eebfdc17SMatthias Kaehlcke TPS65217_WLEDCTRL1_ISEL, TPS65217_PROTECT_NONE); 133eebfdc17SMatthias Kaehlcke if (rc) { 134eebfdc17SMatthias Kaehlcke dev_err(tps65217_bl->dev, 135eebfdc17SMatthias Kaehlcke "failed to select ISET2 current level: %d\n", 136eebfdc17SMatthias Kaehlcke rc); 137eebfdc17SMatthias Kaehlcke return rc; 138eebfdc17SMatthias Kaehlcke } 139eebfdc17SMatthias Kaehlcke 140eebfdc17SMatthias Kaehlcke dev_dbg(tps65217_bl->dev, "selected ISET2 current level\n"); 141eebfdc17SMatthias Kaehlcke 142eebfdc17SMatthias Kaehlcke break; 143eebfdc17SMatthias Kaehlcke 144eebfdc17SMatthias Kaehlcke default: 145eebfdc17SMatthias Kaehlcke dev_err(tps65217_bl->dev, 146eebfdc17SMatthias Kaehlcke "invalid value for current level: %d\n", pdata->isel); 147eebfdc17SMatthias Kaehlcke return -EINVAL; 148eebfdc17SMatthias Kaehlcke } 149eebfdc17SMatthias Kaehlcke 150eebfdc17SMatthias Kaehlcke /* set PWM frequency */ 151eebfdc17SMatthias Kaehlcke rc = tps65217_set_bits(tps65217_bl->tps, 152eebfdc17SMatthias Kaehlcke TPS65217_REG_WLEDCTRL1, 153eebfdc17SMatthias Kaehlcke TPS65217_WLEDCTRL1_FDIM_MASK, 154eebfdc17SMatthias Kaehlcke pdata->fdim, 155eebfdc17SMatthias Kaehlcke TPS65217_PROTECT_NONE); 156eebfdc17SMatthias Kaehlcke if (rc) { 157eebfdc17SMatthias Kaehlcke dev_err(tps65217_bl->dev, 158eebfdc17SMatthias Kaehlcke "failed to select PWM dimming frequency: %d\n", 159eebfdc17SMatthias Kaehlcke rc); 160eebfdc17SMatthias Kaehlcke return rc; 161eebfdc17SMatthias Kaehlcke } 162eebfdc17SMatthias Kaehlcke 163eebfdc17SMatthias Kaehlcke return 0; 164eebfdc17SMatthias Kaehlcke } 165eebfdc17SMatthias Kaehlcke 166eebfdc17SMatthias Kaehlcke #ifdef CONFIG_OF 167eebfdc17SMatthias Kaehlcke static struct tps65217_bl_pdata * 168eebfdc17SMatthias Kaehlcke tps65217_bl_parse_dt(struct platform_device *pdev) 169eebfdc17SMatthias Kaehlcke { 170eebfdc17SMatthias Kaehlcke struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent); 1712b12dfa1SJohan Hovold struct device_node *node; 172eebfdc17SMatthias Kaehlcke struct tps65217_bl_pdata *pdata, *err; 173eebfdc17SMatthias Kaehlcke u32 val; 174eebfdc17SMatthias Kaehlcke 1752b12dfa1SJohan Hovold node = of_get_child_by_name(tps->dev->of_node, "backlight"); 176eebfdc17SMatthias Kaehlcke if (!node) 177eebfdc17SMatthias Kaehlcke return ERR_PTR(-ENODEV); 178eebfdc17SMatthias Kaehlcke 179eebfdc17SMatthias Kaehlcke pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); 180eebfdc17SMatthias Kaehlcke if (!pdata) { 181eebfdc17SMatthias Kaehlcke err = ERR_PTR(-ENOMEM); 182eebfdc17SMatthias Kaehlcke goto err; 183eebfdc17SMatthias Kaehlcke } 184eebfdc17SMatthias Kaehlcke 185eebfdc17SMatthias Kaehlcke pdata->isel = TPS65217_BL_ISET1; 186eebfdc17SMatthias Kaehlcke if (!of_property_read_u32(node, "isel", &val)) { 187eebfdc17SMatthias Kaehlcke if (val < TPS65217_BL_ISET1 || 188eebfdc17SMatthias Kaehlcke val > TPS65217_BL_ISET2) { 189eebfdc17SMatthias Kaehlcke dev_err(&pdev->dev, 190eebfdc17SMatthias Kaehlcke "invalid 'isel' value in the device tree\n"); 191eebfdc17SMatthias Kaehlcke err = ERR_PTR(-EINVAL); 192eebfdc17SMatthias Kaehlcke goto err; 193eebfdc17SMatthias Kaehlcke } 194eebfdc17SMatthias Kaehlcke 195eebfdc17SMatthias Kaehlcke pdata->isel = val; 196eebfdc17SMatthias Kaehlcke } 197eebfdc17SMatthias Kaehlcke 198eebfdc17SMatthias Kaehlcke pdata->fdim = TPS65217_BL_FDIM_200HZ; 199eebfdc17SMatthias Kaehlcke if (!of_property_read_u32(node, "fdim", &val)) { 200eebfdc17SMatthias Kaehlcke switch (val) { 201eebfdc17SMatthias Kaehlcke case 100: 202eebfdc17SMatthias Kaehlcke pdata->fdim = TPS65217_BL_FDIM_100HZ; 203eebfdc17SMatthias Kaehlcke break; 204eebfdc17SMatthias Kaehlcke 205eebfdc17SMatthias Kaehlcke case 200: 206eebfdc17SMatthias Kaehlcke pdata->fdim = TPS65217_BL_FDIM_200HZ; 207eebfdc17SMatthias Kaehlcke break; 208eebfdc17SMatthias Kaehlcke 209eebfdc17SMatthias Kaehlcke case 500: 210eebfdc17SMatthias Kaehlcke pdata->fdim = TPS65217_BL_FDIM_500HZ; 211eebfdc17SMatthias Kaehlcke break; 212eebfdc17SMatthias Kaehlcke 213eebfdc17SMatthias Kaehlcke case 1000: 214eebfdc17SMatthias Kaehlcke pdata->fdim = TPS65217_BL_FDIM_1000HZ; 215eebfdc17SMatthias Kaehlcke break; 216eebfdc17SMatthias Kaehlcke 217eebfdc17SMatthias Kaehlcke default: 218eebfdc17SMatthias Kaehlcke dev_err(&pdev->dev, 219eebfdc17SMatthias Kaehlcke "invalid 'fdim' value in the device tree\n"); 220eebfdc17SMatthias Kaehlcke err = ERR_PTR(-EINVAL); 221eebfdc17SMatthias Kaehlcke goto err; 222eebfdc17SMatthias Kaehlcke } 223eebfdc17SMatthias Kaehlcke } 224eebfdc17SMatthias Kaehlcke 2254d22f8c3SMatus Ujhelyi if (!of_property_read_u32(node, "default-brightness", &val)) { 2265158cc2dSChristos Gkekas if (val > 100) { 2274d22f8c3SMatus Ujhelyi dev_err(&pdev->dev, 2284d22f8c3SMatus Ujhelyi "invalid 'default-brightness' value in the device tree\n"); 2294d22f8c3SMatus Ujhelyi err = ERR_PTR(-EINVAL); 2304d22f8c3SMatus Ujhelyi goto err; 2314d22f8c3SMatus Ujhelyi } 2324d22f8c3SMatus Ujhelyi 2334d22f8c3SMatus Ujhelyi pdata->dft_brightness = val; 2344d22f8c3SMatus Ujhelyi } 2354d22f8c3SMatus Ujhelyi 236eebfdc17SMatthias Kaehlcke of_node_put(node); 237eebfdc17SMatthias Kaehlcke 238eebfdc17SMatthias Kaehlcke return pdata; 239eebfdc17SMatthias Kaehlcke 240eebfdc17SMatthias Kaehlcke err: 241eebfdc17SMatthias Kaehlcke of_node_put(node); 242eebfdc17SMatthias Kaehlcke 243eebfdc17SMatthias Kaehlcke return err; 244eebfdc17SMatthias Kaehlcke } 245eebfdc17SMatthias Kaehlcke #else 246eebfdc17SMatthias Kaehlcke static struct tps65217_bl_pdata * 247eebfdc17SMatthias Kaehlcke tps65217_bl_parse_dt(struct platform_device *pdev) 248eebfdc17SMatthias Kaehlcke { 249eebfdc17SMatthias Kaehlcke return NULL; 250eebfdc17SMatthias Kaehlcke } 251eebfdc17SMatthias Kaehlcke #endif 252eebfdc17SMatthias Kaehlcke 253eebfdc17SMatthias Kaehlcke static int tps65217_bl_probe(struct platform_device *pdev) 254eebfdc17SMatthias Kaehlcke { 255eebfdc17SMatthias Kaehlcke int rc; 256eebfdc17SMatthias Kaehlcke struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent); 257eebfdc17SMatthias Kaehlcke struct tps65217_bl *tps65217_bl; 258eebfdc17SMatthias Kaehlcke struct tps65217_bl_pdata *pdata; 259eebfdc17SMatthias Kaehlcke struct backlight_properties bl_props; 260eebfdc17SMatthias Kaehlcke 261eebfdc17SMatthias Kaehlcke pdata = tps65217_bl_parse_dt(pdev); 262eebfdc17SMatthias Kaehlcke if (IS_ERR(pdata)) 263eebfdc17SMatthias Kaehlcke return PTR_ERR(pdata); 264eebfdc17SMatthias Kaehlcke 265eebfdc17SMatthias Kaehlcke tps65217_bl = devm_kzalloc(&pdev->dev, sizeof(*tps65217_bl), 266eebfdc17SMatthias Kaehlcke GFP_KERNEL); 2673d8e4b40SJingoo Han if (tps65217_bl == NULL) 268eebfdc17SMatthias Kaehlcke return -ENOMEM; 269eebfdc17SMatthias Kaehlcke 270eebfdc17SMatthias Kaehlcke tps65217_bl->tps = tps; 271eebfdc17SMatthias Kaehlcke tps65217_bl->dev = &pdev->dev; 272eebfdc17SMatthias Kaehlcke tps65217_bl->is_enabled = false; 273eebfdc17SMatthias Kaehlcke 274eebfdc17SMatthias Kaehlcke rc = tps65217_bl_hw_init(tps65217_bl, pdata); 275eebfdc17SMatthias Kaehlcke if (rc) 276eebfdc17SMatthias Kaehlcke return rc; 277eebfdc17SMatthias Kaehlcke 278eebfdc17SMatthias Kaehlcke memset(&bl_props, 0, sizeof(struct backlight_properties)); 279eebfdc17SMatthias Kaehlcke bl_props.type = BACKLIGHT_RAW; 280eebfdc17SMatthias Kaehlcke bl_props.max_brightness = 100; 281eebfdc17SMatthias Kaehlcke 282626d0908SJingoo Han tps65217_bl->bl = devm_backlight_device_register(&pdev->dev, pdev->name, 283eebfdc17SMatthias Kaehlcke tps65217_bl->dev, tps65217_bl, 284eebfdc17SMatthias Kaehlcke &tps65217_bl_ops, &bl_props); 285eebfdc17SMatthias Kaehlcke if (IS_ERR(tps65217_bl->bl)) { 286eebfdc17SMatthias Kaehlcke dev_err(tps65217_bl->dev, 287eebfdc17SMatthias Kaehlcke "registration of backlight device failed: %d\n", rc); 288eebfdc17SMatthias Kaehlcke return PTR_ERR(tps65217_bl->bl); 289eebfdc17SMatthias Kaehlcke } 290eebfdc17SMatthias Kaehlcke 2914d22f8c3SMatus Ujhelyi tps65217_bl->bl->props.brightness = pdata->dft_brightness; 2924d22f8c3SMatus Ujhelyi backlight_update_status(tps65217_bl->bl); 293c6bed9deSAxel Lin platform_set_drvdata(pdev, tps65217_bl); 294eebfdc17SMatthias Kaehlcke 295eebfdc17SMatthias Kaehlcke return 0; 296eebfdc17SMatthias Kaehlcke } 297eebfdc17SMatthias Kaehlcke 298fcf13f0bSEnric Balletbo i Serra #ifdef CONFIG_OF 299fcf13f0bSEnric Balletbo i Serra static const struct of_device_id tps65217_bl_of_match[] = { 300fcf13f0bSEnric Balletbo i Serra { .compatible = "ti,tps65217-bl", }, 301fcf13f0bSEnric Balletbo i Serra { /* sentinel */ }, 302fcf13f0bSEnric Balletbo i Serra }; 303fcf13f0bSEnric Balletbo i Serra MODULE_DEVICE_TABLE(of, tps65217_bl_of_match); 304fcf13f0bSEnric Balletbo i Serra #endif 305fcf13f0bSEnric Balletbo i Serra 306eebfdc17SMatthias Kaehlcke static struct platform_driver tps65217_bl_driver = { 307eebfdc17SMatthias Kaehlcke .probe = tps65217_bl_probe, 308eebfdc17SMatthias Kaehlcke .driver = { 309eebfdc17SMatthias Kaehlcke .name = "tps65217-bl", 310fcf13f0bSEnric Balletbo i Serra .of_match_table = of_match_ptr(tps65217_bl_of_match), 311eebfdc17SMatthias Kaehlcke }, 312eebfdc17SMatthias Kaehlcke }; 313eebfdc17SMatthias Kaehlcke 314c6bed9deSAxel Lin module_platform_driver(tps65217_bl_driver); 315eebfdc17SMatthias Kaehlcke 316eebfdc17SMatthias Kaehlcke MODULE_DESCRIPTION("TPS65217 Backlight driver"); 317eebfdc17SMatthias Kaehlcke MODULE_LICENSE("GPL v2"); 318eebfdc17SMatthias Kaehlcke MODULE_AUTHOR("Matthias Kaehlcke <matthias@kaehlcke.net>"); 319