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; 188 struct tps65217_bl_pdata *pdata, *err; 189 u32 val; 190 191 node = of_get_child_by_name(tps->dev->of_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 > 100) { 243 dev_err(&pdev->dev, 244 "invalid 'default-brightness' value in the device tree\n"); 245 err = ERR_PTR(-EINVAL); 246 goto err; 247 } 248 249 pdata->dft_brightness = val; 250 } 251 252 of_node_put(node); 253 254 return pdata; 255 256 err: 257 of_node_put(node); 258 259 return err; 260 } 261 #else 262 static struct tps65217_bl_pdata * 263 tps65217_bl_parse_dt(struct platform_device *pdev) 264 { 265 return NULL; 266 } 267 #endif 268 269 static int tps65217_bl_probe(struct platform_device *pdev) 270 { 271 int rc; 272 struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent); 273 struct tps65217_bl *tps65217_bl; 274 struct tps65217_bl_pdata *pdata; 275 struct backlight_properties bl_props; 276 277 pdata = tps65217_bl_parse_dt(pdev); 278 if (IS_ERR(pdata)) 279 return PTR_ERR(pdata); 280 281 tps65217_bl = devm_kzalloc(&pdev->dev, sizeof(*tps65217_bl), 282 GFP_KERNEL); 283 if (tps65217_bl == NULL) 284 return -ENOMEM; 285 286 tps65217_bl->tps = tps; 287 tps65217_bl->dev = &pdev->dev; 288 tps65217_bl->is_enabled = false; 289 290 rc = tps65217_bl_hw_init(tps65217_bl, pdata); 291 if (rc) 292 return rc; 293 294 memset(&bl_props, 0, sizeof(struct backlight_properties)); 295 bl_props.type = BACKLIGHT_RAW; 296 bl_props.max_brightness = 100; 297 298 tps65217_bl->bl = devm_backlight_device_register(&pdev->dev, pdev->name, 299 tps65217_bl->dev, tps65217_bl, 300 &tps65217_bl_ops, &bl_props); 301 if (IS_ERR(tps65217_bl->bl)) { 302 dev_err(tps65217_bl->dev, 303 "registration of backlight device failed: %d\n", rc); 304 return PTR_ERR(tps65217_bl->bl); 305 } 306 307 tps65217_bl->bl->props.brightness = pdata->dft_brightness; 308 backlight_update_status(tps65217_bl->bl); 309 platform_set_drvdata(pdev, tps65217_bl); 310 311 return 0; 312 } 313 314 #ifdef CONFIG_OF 315 static const struct of_device_id tps65217_bl_of_match[] = { 316 { .compatible = "ti,tps65217-bl", }, 317 { /* sentinel */ }, 318 }; 319 MODULE_DEVICE_TABLE(of, tps65217_bl_of_match); 320 #endif 321 322 static struct platform_driver tps65217_bl_driver = { 323 .probe = tps65217_bl_probe, 324 .driver = { 325 .name = "tps65217-bl", 326 .of_match_table = of_match_ptr(tps65217_bl_of_match), 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