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