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