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