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