319 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * tps65217_bl.c4 *5 * TPS65217 backlight driver6 *7 * Copyright (C) 2012 Matthias Kaehlcke8 * 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 20struct tps65217_bl {21 struct tps65217 *tps;22 struct device *dev;23 struct backlight_device *bl;24 bool is_enabled;25};26 27static 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 47static 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 68static 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 96static const struct backlight_ops tps65217_bl_ops = {97 .options = BL_CORE_SUSPENDRESUME,98 .update_status = tps65217_bl_update_status,99};100 101static 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_OF167static struct tps65217_bl_pdata *168tps65217_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 240err:241 of_node_put(node);242 243 return err;244}245#else246static struct tps65217_bl_pdata *247tps65217_bl_parse_dt(struct platform_device *pdev)248{249 return NULL;250}251#endif252 253static 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_OF299static const struct of_device_id tps65217_bl_of_match[] = {300 { .compatible = "ti,tps65217-bl", },301 { /* sentinel */ },302};303MODULE_DEVICE_TABLE(of, tps65217_bl_of_match);304#endif305 306static 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 314module_platform_driver(tps65217_bl_driver);315 316MODULE_DESCRIPTION("TPS65217 Backlight driver");317MODULE_LICENSE("GPL v2");318MODULE_AUTHOR("Matthias Kaehlcke <matthias@kaehlcke.net>");319