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