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 28 struct tps65217_bl { 29 struct tps65217 *tps; 30 struct device *dev; 31 struct backlight_device *bl; 32 bool is_enabled; 33 }; 34 35 static 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 55 static 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 76 static 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 = bl->props.brightness; 81 82 if (bl->props.state & BL_CORE_SUSPENDED) 83 brightness = 0; 84 85 if ((bl->props.power != FB_BLANK_UNBLANK) || 86 (bl->props.fb_blank != FB_BLANK_UNBLANK)) 87 /* framebuffer in low power mode or blanking active */ 88 brightness = 0; 89 90 if (brightness > 0) { 91 rc = tps65217_reg_write(tps65217_bl->tps, 92 TPS65217_REG_WLEDCTRL2, 93 brightness - 1, 94 TPS65217_PROTECT_NONE); 95 if (rc) { 96 dev_err(tps65217_bl->dev, 97 "failed to set brightness level: %d\n", rc); 98 return rc; 99 } 100 101 dev_dbg(tps65217_bl->dev, "brightness set to %d\n", brightness); 102 103 if (!tps65217_bl->is_enabled) 104 rc = tps65217_bl_enable(tps65217_bl); 105 } else { 106 rc = tps65217_bl_disable(tps65217_bl); 107 } 108 109 return rc; 110 } 111 112 static int tps65217_bl_get_brightness(struct backlight_device *bl) 113 { 114 return bl->props.brightness; 115 } 116 117 static const struct backlight_ops tps65217_bl_ops = { 118 .options = BL_CORE_SUSPENDRESUME, 119 .update_status = tps65217_bl_update_status, 120 .get_brightness = tps65217_bl_get_brightness 121 }; 122 123 static int tps65217_bl_hw_init(struct tps65217_bl *tps65217_bl, 124 struct tps65217_bl_pdata *pdata) 125 { 126 int rc; 127 128 rc = tps65217_bl_disable(tps65217_bl); 129 if (rc) 130 return rc; 131 132 switch (pdata->isel) { 133 case TPS65217_BL_ISET1: 134 /* select ISET_1 current level */ 135 rc = tps65217_clear_bits(tps65217_bl->tps, 136 TPS65217_REG_WLEDCTRL1, 137 TPS65217_WLEDCTRL1_ISEL, 138 TPS65217_PROTECT_NONE); 139 if (rc) { 140 dev_err(tps65217_bl->dev, 141 "failed to select ISET1 current level: %d)\n", 142 rc); 143 return rc; 144 } 145 146 dev_dbg(tps65217_bl->dev, "selected ISET1 current level\n"); 147 148 break; 149 150 case TPS65217_BL_ISET2: 151 /* select ISET2 current level */ 152 rc = tps65217_set_bits(tps65217_bl->tps, TPS65217_REG_WLEDCTRL1, 153 TPS65217_WLEDCTRL1_ISEL, 154 TPS65217_WLEDCTRL1_ISEL, TPS65217_PROTECT_NONE); 155 if (rc) { 156 dev_err(tps65217_bl->dev, 157 "failed to select ISET2 current level: %d\n", 158 rc); 159 return rc; 160 } 161 162 dev_dbg(tps65217_bl->dev, "selected ISET2 current level\n"); 163 164 break; 165 166 default: 167 dev_err(tps65217_bl->dev, 168 "invalid value for current level: %d\n", pdata->isel); 169 return -EINVAL; 170 } 171 172 /* set PWM frequency */ 173 rc = tps65217_set_bits(tps65217_bl->tps, 174 TPS65217_REG_WLEDCTRL1, 175 TPS65217_WLEDCTRL1_FDIM_MASK, 176 pdata->fdim, 177 TPS65217_PROTECT_NONE); 178 if (rc) { 179 dev_err(tps65217_bl->dev, 180 "failed to select PWM dimming frequency: %d\n", 181 rc); 182 return rc; 183 } 184 185 return 0; 186 } 187 188 #ifdef CONFIG_OF 189 static struct tps65217_bl_pdata * 190 tps65217_bl_parse_dt(struct platform_device *pdev) 191 { 192 struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent); 193 struct device_node *node = of_node_get(tps->dev->of_node); 194 struct tps65217_bl_pdata *pdata, *err; 195 u32 val; 196 197 node = of_find_node_by_name(node, "backlight"); 198 if (!node) 199 return ERR_PTR(-ENODEV); 200 201 pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); 202 if (!pdata) { 203 err = ERR_PTR(-ENOMEM); 204 goto err; 205 } 206 207 pdata->isel = TPS65217_BL_ISET1; 208 if (!of_property_read_u32(node, "isel", &val)) { 209 if (val < TPS65217_BL_ISET1 || 210 val > TPS65217_BL_ISET2) { 211 dev_err(&pdev->dev, 212 "invalid 'isel' value in the device tree\n"); 213 err = ERR_PTR(-EINVAL); 214 goto err; 215 } 216 217 pdata->isel = val; 218 } 219 220 pdata->fdim = TPS65217_BL_FDIM_200HZ; 221 if (!of_property_read_u32(node, "fdim", &val)) { 222 switch (val) { 223 case 100: 224 pdata->fdim = TPS65217_BL_FDIM_100HZ; 225 break; 226 227 case 200: 228 pdata->fdim = TPS65217_BL_FDIM_200HZ; 229 break; 230 231 case 500: 232 pdata->fdim = TPS65217_BL_FDIM_500HZ; 233 break; 234 235 case 1000: 236 pdata->fdim = TPS65217_BL_FDIM_1000HZ; 237 break; 238 239 default: 240 dev_err(&pdev->dev, 241 "invalid 'fdim' value in the device tree\n"); 242 err = ERR_PTR(-EINVAL); 243 goto err; 244 } 245 } 246 247 if (!of_property_read_u32(node, "default-brightness", &val)) { 248 if (val < 0 || 249 val > 100) { 250 dev_err(&pdev->dev, 251 "invalid 'default-brightness' value in the device tree\n"); 252 err = ERR_PTR(-EINVAL); 253 goto err; 254 } 255 256 pdata->dft_brightness = val; 257 } 258 259 of_node_put(node); 260 261 return pdata; 262 263 err: 264 of_node_put(node); 265 266 return err; 267 } 268 #else 269 static struct tps65217_bl_pdata * 270 tps65217_bl_parse_dt(struct platform_device *pdev) 271 { 272 return NULL; 273 } 274 #endif 275 276 static int tps65217_bl_probe(struct platform_device *pdev) 277 { 278 int rc; 279 struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent); 280 struct tps65217_bl *tps65217_bl; 281 struct tps65217_bl_pdata *pdata; 282 struct backlight_properties bl_props; 283 284 if (tps->dev->of_node) { 285 pdata = tps65217_bl_parse_dt(pdev); 286 if (IS_ERR(pdata)) 287 return PTR_ERR(pdata); 288 } else { 289 pdata = dev_get_platdata(&pdev->dev); 290 if (!pdata) { 291 dev_err(&pdev->dev, "no platform data provided\n"); 292 return -EINVAL; 293 } 294 } 295 296 tps65217_bl = devm_kzalloc(&pdev->dev, sizeof(*tps65217_bl), 297 GFP_KERNEL); 298 if (tps65217_bl == NULL) 299 return -ENOMEM; 300 301 tps65217_bl->tps = tps; 302 tps65217_bl->dev = &pdev->dev; 303 tps65217_bl->is_enabled = false; 304 305 rc = tps65217_bl_hw_init(tps65217_bl, pdata); 306 if (rc) 307 return rc; 308 309 memset(&bl_props, 0, sizeof(struct backlight_properties)); 310 bl_props.type = BACKLIGHT_RAW; 311 bl_props.max_brightness = 100; 312 313 tps65217_bl->bl = devm_backlight_device_register(&pdev->dev, pdev->name, 314 tps65217_bl->dev, tps65217_bl, 315 &tps65217_bl_ops, &bl_props); 316 if (IS_ERR(tps65217_bl->bl)) { 317 dev_err(tps65217_bl->dev, 318 "registration of backlight device failed: %d\n", rc); 319 return PTR_ERR(tps65217_bl->bl); 320 } 321 322 tps65217_bl->bl->props.brightness = pdata->dft_brightness; 323 backlight_update_status(tps65217_bl->bl); 324 platform_set_drvdata(pdev, tps65217_bl); 325 326 return 0; 327 } 328 329 static struct platform_driver tps65217_bl_driver = { 330 .probe = tps65217_bl_probe, 331 .driver = { 332 .owner = THIS_MODULE, 333 .name = "tps65217-bl", 334 }, 335 }; 336 337 module_platform_driver(tps65217_bl_driver); 338 339 MODULE_DESCRIPTION("TPS65217 Backlight driver"); 340 MODULE_LICENSE("GPL v2"); 341 MODULE_AUTHOR("Matthias Kaehlcke <matthias@kaehlcke.net>"); 342